πŸ“œ ⬆️ ⬇️

Configure two-way RSA and GOST authentication in the application on JBoss EAP 7



Hello!

Security in the modern business information environment is one of the primary roles. Needless to say, in the Bank, where most of the products are built on Digital technologies, and at stake is trust, peace of mind and customer welfare, information security is put in the vanguard.

In this article I will tell you how we use one of the information security tools in our activities.

Authentication is the process of authenticating a subject by its unique identifier and verifying the right to access the information system.
')
Two-way authentication is mutual verification and authentication, in which the information system is also required to confirm its authenticity to the user.

The Red Hat JBoss Enterprise Application Platform server is one of the common and popular platforms for building enterprise applications in Java.

To configure, take the final release of JBoss EAP 7.0.0 from 10.05.2016 from a free source: Red Hat JBoss Enterprise Application Platform

If you have Red Hat registration, you can download additional patches: Red Hat Customer Portal and install your own JBoss EAP instance through the console (http: // localhost: 9990 / console / App.html # patching)



Formulation of the problem


For a corporate application with a thin client in a web browser, middle or backend servlet, which runs on JBoss EAP, you need to configure two-way authentication using TLSv1.2 protocol using foreign cryptographic algorithms.

The task is complicated by the fact that the application on the JBoss server, in turn, interacts with, for example, the CryptoPro Certification Center software and hardware complex, which requires a two-way authentication procedure using the Transport Layer Security Protocol (TLSv1.0) using Russian cryptographic algorithms.

It turns out that in one application on the JBoss server you need to make friends with domestic and foreign cryptography.

Authentication should be performed by the combined method with the Public Key Infrastructure (PKI) tools, i.e. using client and server certificates.

Preparing RSA Authentication Keys


To authenticate the client and confirm to the client the authenticity of the server itself, both parties need to have their own key pairs with public key certificates. As well as the storage of root trusted certificates, where the root certificates of certification centers that issued the final authentication certificates should be installed.



Certification centers for server and client may be different.

The user must trust the certification authority that issued the certificate for the server.

The server must trust the certification authority that issued the certificate for the client.



There are many tools and tools with good descriptions and examples that allow you to work with repositories and generate keys and certificate requests (keytool, openSSL, Portecle, XCA). Therefore, the process of preparing the repositories and issuing certificates is skipped here.

Here, for example, is a good article and step-by-step instructions on how to configure Two-way SSL with TLS1.2 for Oracle SOA Suite using openSSL and keytool. But, truth, it partially lost its relevance and ceased to reflect the whole picture. Certificates issued for it will not be correctly perceived in web browsers. For example, for Google Chrome browser version 58 and further, the subjectAlternativeName attribute of the Extensions block will be used to check the domain name in the TLS certificate, rather than commonName, as before. Now the browser will give an error:





Error: "Subject Alternative Name Missing" or NET :: ERR_CERT_COMMON_NAME_INVALID or "Your connection is not private"

if the server receives an SSL certificate without a domain in the alternative name of the certification entity.

This is what Google Chrome Answer says about it.



How to add subjectAlternativeName to openSL certificate - here and here

Of course, this method of obtaining TLS certificates is only suitable for internal corporate applications.

To interact with external clients, you need to contact trusted certificate authorities whose root certificates are pre-installed in browsers, JREs, operating systems, and client devices. Review of sslshopper.com certification centers

As a result, two repositories were created. The ssl-keystore.jks file with keys and JBoss EAP server certificate for authentication to the client. And ssl-truststore.jks - with a trusted CA root certificate issuing a client authentication certificate.

Preparation of GOST-authentication keys


The mutual authentication process of the JBoss server and CryptoPro server TC is essentially the same. The only difference is in cryptographic algorithms.



Servers exchange certificates with the signature algorithms of GOST R 34.11 / 34.10-2001 and signature hashes of GOST R 34.11-94 instead of the sha256RSA and sha256 used in RSA TLSv1.2, respectively. The root certificate here is the ROOT certificate of the PAC CryptoPro ProC itself.

To generate a key pair and issue a GOST certificate, you can use the cryptoPro certifying center cryptopro.ru/solutions/test-ca

Or, if there is, deployed corporate CAs. At the same time, a cryptographic provider CryptoPro CSP should be installed on the workstation.



After registration on the personal page, you can create requests, manage your certificates.



When generating a key pair, CryptoPro CSP asks to select a repository. This may be the MS Windows registry, eToken, or JaCarta, if drivers are installed and the corresponding eToken PKI Client 5.1 SP1 environment is installed.

JBoss server through Java interfaces implemented in CryptoPro JCP and CryptoPro JCSP can work with the following types of storages:

JCP - CryptoPro Java Provider
KeyStore - CertStore
KeyStore - FloppyStore
KeyStore - HDImageStore
KeyStore - MemoryStore
KeyStore - MemoryStore0- MemoryStore9
KeyStore - OCFBase

JCSP - CryptoPro Java CSP Provider
KeyStore – FLASH
KeyStore - HDIMAGE
KeyStore – REGISTRY
KeyStore – ARDS JaCarta
KeyStore – Aladdin Token JC

In order to have access to eToken, in CryptoPro JCP you must follow the steps given in the CryptoPro instructions. Installation features when using eToken electronic keys

In the control panel of the CryptoPro JCP, the OCF tab will appear and the ability to access the eToken via the OCFStore.



In this example, JBoss EAP will be configured to work with the KeyStore - HDImageStore. This means that if the keys and certificate were generated in the Windows registry, eToken or JaCarta, then you need to solve the problem of transferring them to the HDImageStore format.

If the keys are in the registry or JaCarta, then we use CryptoPro CSP. Service tab "Copy"



Select a container and copy it to eToken.





Further, in the control panel of the CryptoPro JCP, it is possible to view and copy this container from the eToken into the desired HDImageStore format in the OCFStore.

The path to the HDImageStore in the CryptoPro JCP is configured on the Hardware tab



Physically, containers with keys in the HDImageStore format are directories, each of which stores six files with the extension * .key



The JBoss server will use truststore.cpks , which is connected to CryptoPro JCP on the Keys and certificates stores tab, as a trusted root repository with the CAK CryptoPro Pro root certificate.



JBoss server setup


To configure the JBoss EAP 7.0.0 server, all changes need to be made to the jboss-EAP-7.0.0 \ standalone \ configuration \ standalone.xml configuration file

Settings are added to the <system-properties> section, which indicate to the server which providers and storages to use for two-way GOST authentication:

 <property name="java.protocol.handler.pkgs" value="javax.net.ssl"/> <property name="sun.security.ssl.allowUnsafeRenegotiation" value="true"/> <property name="java.util.logging.ConsoleHandler.level" value="ALL"/> <property name="javax.net.ssl.keyStoreProvider" value="JCP"/> <property name="javax.net.ssl.keyStoreType" value="HDImageStore"/> <property name="javax.net.ssl.keyStorePassword" value="myPass"/> <property name="javax.net.ssl.trustStoreProvider" value="JCP"/> <property name="javax.net.ssl.trustStoreType" value="HDImageStore"/> <property name="javax.net.ssl.trustStorePassword" value="myPass"/> <property name="javax.net.ssl.trustStore" value="../standalone/configuration/truststore.cpks"/> <property name="ru.CryptoPro.ssl.SSLLogger.level" value="FINE"/> <property name="ru.CryptoPro.ssl.SSLLogger.handlers" value="java.util.logging.ConsoleHandler"/> <property name="ru.CryptoPro.reprov.enableCRLDP" value="true"/> <property name="com.sun.security.enableCRLDP" value="true"/> <property name="com.ibm.security.enableCRLDP" value="true"/> <property name="ocsp.enable" value="false"/> <property name="com.sun.net.ssl.checkRevocation" value="false"/> <property name="javax.net.ssl.supportGVO" value="true"/> 

To configure RSA TLSv1.2 user authentication and JBoss server in the section

 <management> <security-realms> 

need to add security area:

 <security-realm name="SecureRealm"> <server-identities> <ssl protocol="TLSv1.2"> <keystore path="ssl-keystore.jks" relative-to="jboss.server.config.dir" keystore-password="myPass" alias="my_domain_net"/> </ssl> </server-identities> <authentication> <truststore path="ssl-truststore.jks" relative-to="jboss.server.config.dir" keystore-password="myPass"/> </authentication> </security-realm> 

Thus, for JBoss all necessary keystores and certificates are specified.

In section

 <subsystem xmlns="urn:jboss:domain:undertow:3.1"> <buffer-cache name="default"/> <server name="default-server"> 

to an existing http listener

 <http-listener name="default" max-post-size="104857600" socket-binding="http"/> 

need to add https:

 <https-listener name="https" verify-client="REQUESTED" security-realm="SecureRealm" socket-binding="https"/> 

The verify-client = "REQUESTED" attribute indicates to the server that it is necessary to request the user's certificate in the browser when accessing the server using the https protocol.



The <socket-binding-group name = "standard-sockets" section is used to bind a secure socket:

 <socket-binding name="https" port="${jboss.https.port:8443}"/> 

In Linux OS, where port 8443 is special privileged, you can override it, for example:

 <socket-binding name="https" port="${jboss.https.port:19443}"/> 

If you need to configure the JBoss application on a virtual host, in the section

 <subsystem xmlns="urn:jboss:domain:undertow:3.1"> <buffer-cache name="default"/> <server name="default-server"> 

required to add next to

 <host name="default-host" alias="localhost"> 
setting up your host:

  <host name="myApp" alias="my.domain.net "> <location name="/" handler="welcome-content"/> <filter-ref name="server-header"/> <filter-ref name="x-powered-by-header"/> </host> 

In section

 <subsystem xmlns="urn:jboss:domain:security:1.2"> <security-domains> 

need to add the security domain of our application

 <security-domain name="myApp" cache-type="default"> <authentication> <login-module code="CertificateRoles" flag="required"> <module-option name="verifier" value="org.jboss.security.auth.certs.AnyCertVerifier"/> <module-option name="securityDomain" value="myApp"/> <module-option name="rolesProperties" value="file:/JBOSS/jboss-eap-7.0/standalone/configuration/apps/myApp/roles.properties"/> </login-module> </authentication> <jsse keystore-password="myPass" keystore-url="file:/JBOSS/jboss-eap-7.0/standalone/configuration/ssl-keystore.jks" truststore-password="myPass" truststore-url="file:/JBOSS/jboss-eap-7.0/standalone/configuration/ssl-truststore.jks" client-auth="true"/> </security-domain> 

To authorize users, if necessary, as shown in the example, a login-module can be configured in the security domain, which will determine the role of the user in the application using his authentication certificate. For this, the login module uses the prepared /roles.properties file.

Sample file content - Subject X500Principal = role
#
# user=role1,role2,...
#
C\=RU,\ L\=Moscow,\ O\=Alfabank,\ OU\=ORRPP,\ CN\=Vasiliy\ Burmistrov=admin
C\=RU,\ L\=Moscow,\ O\=Alfabank,\ OU\=ORRPP,\ CN\=Ivan\ Petrov=user

As part of the delivery of CryptoPro JCP there is an instruction for setting up the server JBoss jcp-2.0.39014 \ Doc \ WebServerIntegration \ JBoss \ Howto_set_gost_tls_jboss.docx
JBOSS 6.1.0 Final INTEGRATION (β€œNeo”) and JTLS 2.0. This document deals with the outdated version of JBoss and the user authentication process using the Gost TLS protocol directly on the server. This can be useful if the goal is to raise a channel protected by GOST between the user's browser or another front and application server.

Application setup


In order for the deployment application to be integrated with the settings on the server, you need to make a number of parameters in its configuration files.

The security domain and virtual host of the application are specified in /WEB-INF/jboss-web.xml

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 5.0//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd"> <jboss-web> <security-domain>myApp</security-domain> <virtual-host>myApp</virtual-host> </jboss-web> 

The file /WEB-INF/web.xml configures security restrictions:

 <security-constraint> <web-resource-collection> <web-resource-name>myApp</web-resource-name> <url-pattern>/ui1/*</url-pattern> <url-pattern>/ui2/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>*</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> 

and authentication method:

 <login-config> <auth-method>CLIENT-CERT</auth-method> <realm-name>myApp</realm-name> </login-config> 

As a result of these settings in the application on the JBoss server in Runtime, you can receive from the request of a web browser or other front-system certificate of the authenticated user:

 X509Certificate[] certChain = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); X509Certificate userCert = certChain[0]; 

Interacting with the CAK CryptoPro CA, the application uses the SOAP API, which is described in the documentation of the LTYA.00067-02 20 01-LU (ZhTYAI.00067-02 90 16) "CryptoPro CA TC hardware and software certification center programmer's guide"

The web service is invoked using the Apache Axis 1.4 framework, which implements the JAX-WS interface.

If the store HDImageStore, in addition to authentication keys directly in the CryptoPro CA, also stores keys for other applications and other purposes, then a problem arises. Apache Axis when calling the CryptoPro service, the CA is missing from the repository the first one that was found and, accordingly, authentication fails.

To solve this problem, it was necessary to make its own implementation of SocketFactoryImpl.

To do this, inherit from org.apache.axis.components.net.JSSESocketFactory

And implement the org.apache.axis.components.net.SecureSocketFactory interface

Code of own factory SocketFactoryImpl

 package ru.alfabank.orrpp.common.alfaca.ws; import java.io.FileInputStream; import java.io.IOException; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.Hashtable; import java.util.logging.Logger; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import org.apache.axis.components.net.JSSESocketFactory; import org.apache.axis.components.net.SecureSocketFactory; /** * SSL socket factory. */ public class SocketFactoryImpl extends JSSESocketFactory implements SecureSocketFactory { public SocketFactoryImpl(Hashtable attributes) { super(attributes); } private static final Logger LOGGER = Logger.getLogger(SocketFactoryImpl.class.getName()); private static final String SIGNER_KEY_ALIAS = "signer_CA"; private static final String TRUST_CERT_ALIAS = "trust_CA"; private static final char[] PASSWORD = "myPass".toCharArray(); /** * Read the keystore, init the SSL socket factory * * @throws IOException */ protected void initFactory() throws IOException { try { //Configuration specified in wsdd. SSLContext context = getContext(); sslFactory = context.getSocketFactory(); } catch (Exception e) { if (e instanceof IOException) { throw (IOException) e; } throw new IOException(e.getMessage()); } } /** * gets a SSL Context * * @return SSLContext * @throws Exception */ protected SSLContext getContext() throws Exception { System.setProperty("ssl.KeyManagerFactory.algorithm", "GostX509"); System.setProperty("ssl.TrustManagerFactory.algorithm", "GostX509"); System.setProperty("ssl.SocketFactory.provider", "ru.CryptoPro.ssl.SSLSocketFactoryImpl"); System.setProperty("ssl.ServerSocketFactory.provider", "ru.CryptoPro.ssl.SSLServerSocketFactoryImpl"); System.setProperty("ru.CryptoPro.ssl.Provider", "JCP"); System.out.println("javax.net.ssl.trustStore: " + System.getProperty("javax.net.ssl.trustStore")); KeyStore trustStore = KeyStore.getInstance(System.getProperty("javax.net.ssl.trustStoreType")); String trustStoreFileName = System.getProperty("javax.net.ssl.trustStore"); trustStore.load(trustStoreFileName == null ? null : new FileInputStream(trustStoreFileName), System.getProperty("javax.net.ssl.trustStorePassword").toCharArray()); KeyStore ks = KeyStore.getInstance(System.getProperty("javax.net.ssl.keyStoreType")); String keystoreFileName = System.getProperty("javax.net.ssl.keyStore"); ks.load(keystoreFileName == null ? null : new FileInputStream(keystoreFileName), System.getProperty("javax.net.ssl.keyStorePassword").toCharArray()); X509Certificate cert = (X509Certificate) ks.getCertificate(SIGNER_KEY_ALIAS); LOGGER.info("Signer found: " + ((X509Certificate) cert).getSubjectX500Principal().getName()); PrivateKey privateKey = (PrivateKey) ks.getKey(SIGNER_KEY_ALIAS, PASSWORD); X509Certificate trustCert = (X509Certificate) trustStore.getCertificate(TRUST_CERT_ALIAS); LOGGER.info("Trust found: " + ((X509Certificate) trustCert).getSubjectX500Principal().getName()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("GostX509"); tmf.init(trustStore); SSLContext context = SSLContext.getInstance("GostTLS", "JTLS"); context.init(new KeyManager[] {new KeyManagerImpl(SIGNER_KEY_ALIAS, privateKey, new X509Certificate[] {cert})}, tmf.getTrustManagers(), null); return context; } } 

To force Apache Axis to use its own factory, in the @Stateless bean,
where we construct a call to the service of CryptoPro Pro Center, override the system property:

 AxisProperties.setProperty("axis.socketSecureFactory", "ru.alfabank.orrpp.common.alfaca.ws.SocketFactoryImpl"); 

JRE setup


It would seem that everything is set up, and should work. But no. There remains one more important touch.

In order to authenticate to CryptoPro CA using the Gost TLS protocol from the application on JBoss, the JBoss server must be launched in the JRE with the installed CryptoPro JCP provider.

When installing JCP, the JRE settings are changed. In particular, in jre1.8.0_144 \ lib \ security \ java.security, the KeyManagerFactory and TrustManagerFactory algorithms are changed to Gost. If you leave it like this, then in JBoss an error will appear at the start:

2017-10-12 17:44:13,376 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-7) MSC000001: Failed to start service jboss.security.security-domain.myApp: org.jboss.msc.service.StartException in service jboss.security.security-domain.myApp: WFLYSEC0012: Unable to start the SecurityDomainService service
at org.jboss.as.security.service.SecurityDomainService.start(SecurityDomainService.java:105)
at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:2032)
at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1955)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.security.KeyStoreException: Default store provider (keyStore) is SUN but default config provider (cpSSL) is JCP. Check settings on the tab 'Algorithms' of JCP Pane.
at ru.CryptoPro.ssl.r.(Unknown Source)
at ru.CryptoPro.ssl.KeyManagerFactoryImpl.engineInit(Unknown Source)
at javax.net.ssl.KeyManagerFactory.init(Unknown Source)
at org.jboss.security.JBossJSSESecurityDomain.loadKeyAndTrustStore(JBossJSSESecurityDomain.java:488)
at org.jboss.security.JBossJSSESecurityDomain.reloadKeyAndTrustStore(JBossJSSESecurityDomain.java:335)
at org.jboss.as.security.service.SecurityDomainService.start(SecurityDomainService.java:102)
... 5 more




For fix, comment in the java.security GostX509 file
#ssl.KeyManagerFactory.algorithm=GostX509
#ssl.TrustManagerFactory.algorithm=GostX509

and return the default settings of the SunX509
ssl.KeyManagerFactory.algorithm=SunX509
ssl.TrustManagerFactory.algorithm=PKIX

results


The application and runtime with two-way authentication are configured, the data transmission channels are encrypted, and reliable connections are established between the system participants.






useful links


  1. Where to download JBoss EAP 7.0.0
  2. Red Hat Customer Portal
  3. Article Two-way SSL with TLS1.2 for Oracle SOA Suite
  4. Google support for "Subject Alternative Name Missing"
  5. How to use openSSL to add subjectAlternativeName to your certificate here and here
  6. Review of certification centers with foreign cryptographic algorithms
  7. Test certification center CryptoPro
  8. Aladdin, where to download eToken or JaCarta support drivers (eToken PKI Client 5.1 SP1)
  9. Instructions for configuring CryptoPro JCP to support eToken

Source: https://habr.com/ru/post/340028/


All Articles