Меню

Ошибка javax net ssl sslhandshakeexception

Whenever we are trying to connect to URL,

if server at the other site is running on https protocol and is mandating that we should communicate via information provided in certificate then
we have following option:

1) ask for the certificate(download the certificate), import this certificate in trustore. Default trustore java uses can be found in Javajdk1.6.0_29jrelibsecuritycacerts, then if we retry to connect to the URL connection would be accepted.

2) In normal business cases, we might be connecting to internal URLS in organizations and we know that they are correct.
In such cases, you trust that it is the correct URL, In such cases above, code can be used which will not mandate to store the certificate to connect to particular URL.

for the point no 2 we have to follow below steps :

1) write below method which sets HostnameVerifier for HttpsURLConnection which returns true for all cases meaning we are trusting the trustStore.

  // trusting all certificate 
 public void doTrustToCertificates() throws Exception {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }
                }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
                }
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

2) write below method, which calls doTrustToCertificates before trying to connect to URL

    // connecting to URL
    public void connectToUrl(){
     doTrustToCertificates();//  
     URL url = new URL("https://www.example.com");
     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     System.out.println("ResponseCode ="+conn.getResponseCode());
   }

This call will return response code = 200 means connection is successful.

For more detail and sample example you can refer to URL.

Whenever we are trying to connect to URL,

if server at the other site is running on https protocol and is mandating that we should communicate via information provided in certificate then
we have following option:

1) ask for the certificate(download the certificate), import this certificate in trustore. Default trustore java uses can be found in Javajdk1.6.0_29jrelibsecuritycacerts, then if we retry to connect to the URL connection would be accepted.

2) In normal business cases, we might be connecting to internal URLS in organizations and we know that they are correct.
In such cases, you trust that it is the correct URL, In such cases above, code can be used which will not mandate to store the certificate to connect to particular URL.

for the point no 2 we have to follow below steps :

1) write below method which sets HostnameVerifier for HttpsURLConnection which returns true for all cases meaning we are trusting the trustStore.

  // trusting all certificate 
 public void doTrustToCertificates() throws Exception {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }
                }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
                }
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

2) write below method, which calls doTrustToCertificates before trying to connect to URL

    // connecting to URL
    public void connectToUrl(){
     doTrustToCertificates();//  
     URL url = new URL("https://www.example.com");
     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     System.out.println("ResponseCode ="+conn.getResponseCode());
   }

This call will return response code = 200 means connection is successful.

For more detail and sample example you can refer to URL.

Whenever we are trying to connect to URL,

if server at the other site is running on https protocol and is mandating that we should communicate via information provided in certificate then
we have following option:

1) ask for the certificate(download the certificate), import this certificate in trustore. Default trustore java uses can be found in Javajdk1.6.0_29jrelibsecuritycacerts, then if we retry to connect to the URL connection would be accepted.

2) In normal business cases, we might be connecting to internal URLS in organizations and we know that they are correct.
In such cases, you trust that it is the correct URL, In such cases above, code can be used which will not mandate to store the certificate to connect to particular URL.

for the point no 2 we have to follow below steps :

1) write below method which sets HostnameVerifier for HttpsURLConnection which returns true for all cases meaning we are trusting the trustStore.

  // trusting all certificate 
 public void doTrustToCertificates() throws Exception {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }
                }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
                }
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

2) write below method, which calls doTrustToCertificates before trying to connect to URL

    // connecting to URL
    public void connectToUrl(){
     doTrustToCertificates();//  
     URL url = new URL("https://www.example.com");
     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     System.out.println("ResponseCode ="+conn.getResponseCode());
   }

This call will return response code = 200 means connection is successful.

For more detail and sample example you can refer to URL.

  1. SSL Handshakes in Java
  2. Fix the SSLHandshakeException Because of Missing Server Certificate
  3. Fix the SSLHandShakeException Because of Untrusted Server Certificate
  4. Fix the SSLHandShakeException Because of Incorrect Certificate
  5. Fix the SSLHandShakeException Because of Incompatible SSL Version and Cipher Suite

Fix the Javax.Net.SSL.SSLHandshakeException Error

This tutorial demonstrates the javax.net.ssl.SSLHandshakeException error in Java.

SSL Handshakes in Java

The SSL Handshakes are used for the client and server to establish trust and logistics required to secure the connection over the internet. There are typical steps in SSL Handshake operations which are needed to be followed:

  1. First, the client will provide the list of all possible cipher suites and SSL versions.
  2. The server will then agree on the particular cipher suite and SSL version, which will respond with a certificate.
  3. Then, the client will extract the public key from the given certificate and respond with the new encrypted pre-master key.
  4. Then, the server will use the private key to decrypt the pre-master key.
  5. Then, the client and server will compute the shared secret using the pre-master key together.
  6. Finally, the client and server will exchange messages which confirm the successful encryption and decryption of the shared secret.

The SSL Handshakes have two types. The first is the one-way SSL, which leaves the server to trust all the clients, and the second is the two-way SSL in which the client and server must accept each other’s certificates.

After understanding the SSL Handshakes, we can now discuss the SSLHandShakeException in detail. There are two scenarios of SSLHandShakeException, which are given below.

Fix the SSLHandshakeException Because of Missing Server Certificate

If an SSL Handshake operation when the client is connecting to the server did not receive any certificate, it would throw the SSLHandShakeException as mentioned below:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
  Received fatal alert: handshake_failure

To solve this issue, make sure you follow all the steps above. This problem occurs when the Keystore or the system properties are not entered properly.

Keystores are the certificates provided by the authorities, or we can also create our Keystores by using the keytool functionality of JDK. Here is an example for the Keystore:

$ keytool -genkey -keypass password 
                  -storepass password 
                  -keystore Server_Keystore.jks

The above keytool code is written in the Keystore file. Now the keytool can be used to extract a public certificate from the Keystore file which was generated above:

$ keytool -export -storepass password 
                  -file NewServer.cer 
                  -keystore Server_Keystore.jks

The above code will export the public certificate from the Keystore as a file NewServer.cer. Now, we can add it to the Truststore for the client:

$ keytool -import -v -trustcacerts 
                     -file NewServer.cer 
                     -keypass password 
                     -storepass password 
                     -keystore Client_Truststore.jks

Now the Keystore for the server and Truststore for the client are generated. We can pass them as system properties to the server with a command:

-Djavax.net.ssl.keyStore=Client_Keystore.jks -Djavax.net.ssl.keyStorePassword=password

It is necessary for the system property. The Keystore file path must be absolute or place the Keystore file in the same directory from where the command is invoked.

The relative paths are not supported. Once you follow this process, the missing certificate error will be solved, and there will be no more SSLHandShakeException.

Fix the SSLHandShakeException Because of Untrusted Server Certificate

The other reason for SSLHandShakeException is an untrusted server certificate. When a server is using a self-signed certificate that is not signed by authorities, it will throw the following error:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
  sun.security.validator.ValidatorException:
  PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
  unable to find valid certification path to requested target

This exception will be thrown whenever the certificate is signed by any entity other than the default store. The default Truststore in JDK ships the information about common certificates in use.

This issue can be solved by forcing the client to trust the certificate presented by the server. We need to use the Truststore we generated above and pass them as system properties to the client:

-Djavax.net.ssl.trustStore=Client_Truststore.jks -Djavax.net.ssl.trustStorePassword=password

This will solve the exception, but it is not an ideal situation. In an ideal situation, we can use the self-signed certificate, which should be certified by the Certificate Authority (CA), then the client can trust them by default.

Fix the SSLHandShakeException Because of Incorrect Certificate

A handshake can also fail because of an incorrect certificate. When a certificate is not created properly, it will throw the SSLHandShakeException:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
    java.security.cert.CertificateException:
    No name matching localhost found

To check if the certificate is created properly, run the following command:

keytool -v -list -keystore Server_Keystore.jks

The above command will show the details of the Keystore owner:

...
Owner: CN=localhost, OU=technology, O=delftstack, L=city, ST=state, C=xx
..

The owner’s CN must match the server’s CN, and if it doesn’t match, it will throw the same exception shown above as it is generated because of the different CN.

Fix the SSLHandShakeException Because of Incompatible SSL Version and Cipher Suite

While an SSL handshake operation, it is possible that there will be various cryptographic protocols like different versions of SSL, TLS, etc. While the client and server must agree on cryptographic protocols and versions on a handshake, the SSL is superseded by the TLS for its cryptographic strength.

Now, for example, if the server is using the protocol SSL3, and the client is using the protocol TLS1.3, both cannot agree on the cryptographic protocol, and it will throw the SSLHandShakeException:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
  No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

To solve this issue, we must verify that client and server are using either the same or compatible cryptographic protocols.

Similarly, it is also necessary to have the compatible Cipher Suite. While a handshake, the client provides the list of ciphers, and the server will select a cipher to use.

If the server cannot select a suitable cipher, the code will throw the following SSLHandShakeException:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
  Received fatal alert: handshake_failure

Normally the client and server use a variety of cipher suites; that is why this error can occur. The error occurs because a server has chosen a very selective cipher.

To avoid this issue, the server uses a list of selective ciphers, which is also good for security.

SSLHandshakeException appear in logs when there is some error occur while validating the certificate installed in client machine with certificate on server machine. In this post, we will learn about fixing this if you are using Apache HttpClient library to create HttpClient to connect to SSL/TLS secured URLs.

The exception logs will look like this.

Caused by: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:980)
	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1363)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1391)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1375)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:275)
	at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:254)
	at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:117)
	at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:314)
	at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
	at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
	at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
	at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
	at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
	at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:186)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
	at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:88)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:46)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:49)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:509)
	... 61 more
Caused by: java.io.EOFException: SSL peer shut down incorrectly
	at sun.security.ssl.InputRecord.read(InputRecord.java:505)
	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:961)
	... 80 more

I have already posted code fix to bypass SSL matching in earlier post.

Unfortunately, that fix works in TLS and TLS 1.1 protocols. It doesn’t work in TLS 1.2 protocol. So ultimately, you need to fix the certificate issue anyway. There is ‘no code only’ fix for this.

Now there are two ways, you can utilize the imported certificate from server. Either add certificate to the JDK cacerts store; or pass certificate information in JVM aruguments.

1) Import certificate to JDK cacert store

  1. Import the certificate from server.
  2. Use given command to add the certificate to JDK store. (Remove new line characters).
    keytool -import 
    	-noprompt 
    	-trustcacerts 
    	-alias MAVEN-ROOT 
    	-file C:/Users/Lokesh/keys/cert/maven.cer 
    	-keystore "C:/Program Files (x86)/Java/jdk8/jre/lib/security/cacerts" 
    	-storepass changeit
    

Now create HTTP client as given:

public HttpClient createTlsV2HttpClient() throws KeyManagementException, 
				UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {

      SSLContext sslContext = SSLContext.getInstance("TLSv1.2");

      SSLConnectionSocketFactory f = new SSLConnectionSocketFactory(sslContext, new String[] { "TLSv1.2" }, null,
                   						SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

      Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                   		.register("http", PlainConnectionSocketFactory.getSocketFactory())
                   		.register("https", f)
                   		.build();

      PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

      CloseableHttpClient client = HttpClients
      					.custom()
      					.setSSLSocketFactory(f)
                   		.setConnectionManager(cm)
                   		.build();
      return client;
}

Notice the code : SSLContext.getInstance("TLSv1.2"). This code picks up the certificates added to JDK cacert store. So make a note of it.

2) Pass certificate information in JVM aruguments

  1. Import the certicate from server.
  2. Add JVM arguments while starting the server. Change the parameter values as per your application.
    -Djavax.net.ssl.keyStore="C:/Users/Lokeshkeysmaven.jks" 
    -Djavax.net.ssl.keyStorePassword="test" 
    -Djavax.net.ssl.trustStore="C:/Users/Lokeshkeysmaven.jks" 
    -Djavax.net.ssl.trustStorePassword="test" 
    

Now create HTTP client as given:

public HttpClient createTlsV2HttpClient() throws KeyManagementException, 
				UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {

      SSLContext sslContext = SSLContexts.createSystemDefault();

      SSLConnectionSocketFactory f = new SSLConnectionSocketFactory(sslContext, new String[] { "TLSv1.2" }, null,
                   						SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

      Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                   		.register("http", PlainConnectionSocketFactory.getSocketFactory())
                   		.register("https", f)
                   		.build();

      PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

      CloseableHttpClient client = HttpClients
      					.custom()
      					.setSSLSocketFactory(f)
                   		.setConnectionManager(cm)
                   		.build();
      return client;
}

Notice the code : SSLContext.createSystemDefault(). This code picks up the certificates passed as JVM arguments. Again, make a note of it.

Summary

  1. Use SSLContext.getInstance("TLSv1.2") when certificate is added to JDK cacert store.
  2. Use SSLContext.createSystemDefault() when SSL info is passed as JVM argument.

Drop me your questions in comments section.

Happy Learning !!

Сбой SSL рукопожатия

1. обзор

Secured Socket Layer (SSL) — это криптографический протокол, который обеспечивает безопасность связи по сети. In this tutorial, we’ll discuss various scenarios that can result in an SSL handshake failure and how to it.с

2. терминология

Важно отметить, что из-за уязвимостей системы безопасности SSL как стандарт заменяется безопасностью транспортного уровня (TLS). Большинство языков программирования, включая Java, имеют библиотеки для поддержки как SSL, так и TLS.

С момента появления SSL многие продукты и языки, такие как OpenSSL и Java, имели ссылки на SSL, которые они сохраняли даже после вступления в силу TLS. По этой причине в оставшейся части этого руководства мы будем использовать термин SSL для общего обозначения криптографических протоколов.

3. Настроить

Для целей этого руководства мы создадим простые серверные и клиентские приложения, используяthe Java Socket API для имитации сетевого подключения.

3.1. Создание клиента и сервера

В Java мы можем использоватьsockets to establish a communication channel between a server and client over the network. Сокеты являются частью Java Secure Socket Extension (JSSE) в Java.

Начнем с определения простого сервера:

int port = 8443;
ServerSocketFactory factory = SSLServerSocketFactory.getDefault();
try (ServerSocket listener = factory.createServerSocket(port)) {
    SSLServerSocket sslListener = (SSLServerSocket) listener;
    sslListener.setNeedClientAuth(true);
    sslListener.setEnabledCipherSuites(
      new String[] { "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256" });
    sslListener.setEnabledProtocols(
      new String[] { "TLSv1.2" });
    while (true) {
        try (Socket socket = sslListener.accept()) {
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            out.println("Hello World!");
        }
    }
}

Определенный выше сервер возвращает сообщение «Hello World!» Подключенному клиенту.

Затем давайте определим базового клиента, которого мы подключим к нашемуSimpleServer:

String host = "localhost";
int port = 8443;
SocketFactory factory = SSLSocketFactory.getDefault();
try (Socket connection = factory.createSocket(host, port)) {
    ((SSLSocket) connection).setEnabledCipherSuites(
      new String[] { "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256" });
    ((SSLSocket) connection).setEnabledProtocols(
      new String[] { "TLSv1.2" });

    SSLParameters sslParams = new SSLParameters();
    sslParams.setEndpointIdentificationAlgorithm("HTTPS");
    ((SSLSocket) connection).setSSLParameters(sslParams);

    BufferedReader input = new BufferedReader(
      new InputStreamReader(connection.getInputStream()));
    return input.readLine();
}

Наш клиент печатает сообщение, возвращаемое сервером.

3.2. Создание сертификатов в Java

SSL обеспечивает секретность, целостность и аутентичность в сетевых коммуникациях. Сертификаты играют важную роль в установлении подлинности.

Обычно эти сертификаты приобретаются и подписываются центром сертификации, но в этом руководстве мы будем использовать самозаверяющие сертификаты.

Для этого мы можем использоватьkeytool, w, который поставляется с JDK:

$ keytool -genkey -keypass password 
                  -storepass password 
                  -keystore serverkeystore.jks

Приведенная выше команда запускает интерактивную оболочку для сбора информации для сертификата, например Common Name (CN) и Distinguished Name (DN). Когда мы предоставляем всю необходимую информацию, он генерирует файлserverkeystore.jks, который содержит закрытый ключ сервера и его открытый сертификат.

Обратите внимание, чтоserverkeystore.jks  хранится в формате Java Key Store (JKS), который является проприетарным для Java. These days, keytool will remind us that we ought to consider using PKCS#12, which it also supports.

Далее мы можем использоватьkeytool to для извлечения публичного сертификата из сгенерированного файла хранилища ключей:

$ keytool -export -storepass password 
                  -file server.cer 
                  -keystore serverkeystore.jks

Приведенная выше команда экспортирует открытый сертификат из хранилища ключей как файлserver.cer. Давайте использовать экспортированный сертификат для клиента, добавив его в его хранилище доверенных сертификатов:

$ keytool -import -v -trustcacerts 
                     -file server.cer 
                     -keypass password 
                     -storepass password 
                     -keystore clienttruststore.jks

Теперь мы создали хранилище ключей для сервера и соответствующее хранилище доверенных сертификатов для клиента. Мы рассмотрим использование этих сгенерированных файлов, когда будем обсуждать возможные сбои рукопожатия.

И более подробную информацию об использовании хранилища ключей Java можно найти в нашемprevious tutorial.

4. Подтверждение SSL

Подтверждения SSL составляютa mechanism by which a client and server establish the trust and logistics required to secure their connection over the network.

Это очень организованная процедура, и понимание ее подробностей может помочь понять, почему она часто терпит неудачу, о чем мы собираемся рассказать в следующем разделе.

Типичные шаги в рукопожатии SSL:

  1. Клиент предоставляет список возможных версий SSL и наборов шифров для использования

  2. Сервер соглашается на конкретную версию SSL и набор шифров, отвечая своим сертификатом

  3. Клиент извлекает открытый ключ из сертификата и отвечает зашифрованным «предварительным главным ключом»

  4. Сервер расшифровывает «предварительный мастер-ключ», используя свой закрытый ключ

  5. Клиент и сервер вычисляют «общий секрет», используя обмененный «предварительный мастер-ключ»

  6. Клиент и сервер обмениваются сообщениями, подтверждающими успешное шифрование и дешифрование с использованием «общего секрета»

Хотя большинство шагов одинаковы для любого рукопожатия SSL, между односторонним и двусторонним SSL есть небольшая разница. Давайте быстро рассмотрим эти различия.

4.1. Рукопожатие в одностороннем SSL

Если мы ссылаемся на шаги, упомянутые выше, на шаге два упоминается обмен сертификатами. Односторонний SSL требует, чтобы клиент мог доверять серверу через свой открытый сертификат. Этоleaves the server to trust all clients, которые запрашивают соединение. Сервер не может запрашивать и проверять общедоступный сертификат у клиентов, что может представлять угрозу безопасности.

4.2. Рукопожатие в двустороннем SSL

При использовании одностороннего SSL сервер должен доверять всем клиентам. Но двусторонний SSL добавляет возможность для сервера также устанавливать доверенных клиентов. Во время двустороннего рукопожатияboth the client and server must present and accept each other’s public certificates перед установкой успешного соединения.

5. Сценарии отказа рукопожатия

Сделав этот быстрый обзор, мы можем более четко рассмотреть сценарии сбоев.

Рукопожатие SSL при односторонней или двусторонней связи может завершиться неудачей по нескольким причинам. Мы рассмотрим каждую из этих причин, смоделируем сбой и поймем, как мы можем избежать таких сценариев.

В каждом из этих сценариев мы будем использоватьSimpleClient иSimpleServer, которые мы создали ранее.

5.1. Отсутствует сертификат сервера

Давайте попробуем запуститьSimpleServer и подключить его черезSimpleClient. Хотя мы ожидаем увидеть сообщение «Hello World!», Мы представляем исключение:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
  Received fatal alert: handshake_failure

Теперь это означает, что что-то пошло не так. SSLHandshakeException выше, абстрактно,is stating that the client when connecting to the server did not receive any certificate.

Для решения этой проблемы мы будем использовать хранилище ключей, которое мы сгенерировали ранее, передавая их в качестве системных свойств серверу:

-Djavax.net.ssl.keyStore=clientkeystore.jks -Djavax.net.ssl.keyStorePassword=password

Важно отметить, что системное свойство для пути к файлу хранилища ключей должно быть либо абсолютным путем, либо файл хранилища ключей должен быть помещен в тот же каталог, из которого вызывается команда Java для запуска сервера. Java system property for keystore does not support relative paths.с

Помогает ли это нам получить ожидаемый результат? Давайте узнаем в следующем подразделе.

5.2. Сертификат ненадежного сервера

Когда мы снова запустимSimpleServer иSimpleClient с изменениями в предыдущем подразделе, что мы получим в качестве вывода:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
  sun.security.validator.ValidatorException:
  PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
  unable to find valid certification path to requested target

Ну, это не сработало так, как мы ожидали, но похоже, что это не удалось по другой причине.

Эта конкретная ошибка вызвана тем, что наш сервер использует сертификатself-signed, который не подписан центром сертификации (CA).

Really, any time the certificate is signed by something other than what is in the default truststore, we’ll see this error. Склад доверенных сертификатов по умолчанию в JDK обычно поставляется с информацией об используемых общих центрах сертификации.

Чтобы решить эту проблему здесь, нам придется заставитьSimpleClient доверять сертификату, представленномуSimpleServer. Давайте воспользуемся хранилищем доверенных сертификатов, которое мы создали ранее, передав их клиенту как системные свойства:

-Djavax.net.ssl.trustStore=clienttruststore.jks -Djavax.net.ssl.trustStorePassword=password

Обратите внимание, что это не идеальное решение. In an ideal scenario, we should not use a self-signed certificate but a certificate which has been certified by a Certificate Authority (CA) which clients can trust by default.с

Давайте перейдем к следующему подразделу, чтобы узнать, получим ли мы ожидаемый результат сейчас.

5.3. Отсутствует сертификат клиента

Давайте попробуем еще раз запустить SimpleServer и SimpleClient, применив изменения из предыдущих подразделов:

Exception in thread "main" java.net.SocketException:
  Software caused connection abort: recv failed

Опять же, не то, что мы ожидали. SocketException здесь говорит нам, что сервер не может доверять клиенту. Это потому, что мы настроили двусторонний SSL. В нашемSimpleServer we:

((SSLServerSocket) listener).setNeedClientAuth(true);

Приведенный выше код указывает, чтоSSLServerSocket требуется для аутентификации клиента через его открытый сертификат.

Мы можем создать хранилище ключей для клиента и соответствующее хранилище доверенных сертификатов для сервера способом, аналогичным тому, который мы использовали при создании предыдущего хранилища ключей и хранилища доверенных сертификатов.

Мы перезапустим сервер и передадим ему следующие системные свойства:

-Djavax.net.ssl.keyStore=serverkeystore.jks 
    -Djavax.net.ssl.keyStorePassword=password 
    -Djavax.net.ssl.trustStore=servertruststore.jks 
    -Djavax.net.ssl.trustStorePassword=password

Затем мы перезапустим клиент, передав следующие системные свойства:

-Djavax.net.ssl.keyStore=clientkeystore.jks 
    -Djavax.net.ssl.keyStorePassword=password 
    -Djavax.net.ssl.trustStore=clienttruststore.jks 
    -Djavax.net.ssl.trustStorePassword=password

Наконец, у нас есть желаемый результат:

5.4. Неверные сертификаты

Помимо вышеперечисленных ошибок, рукопожатие может потерпеть неудачу из-за множества причин, связанных с тем, как мы создали сертификаты. Одна распространенная ошибка связана с неправильным CN. Давайте подробно рассмотрим созданное нами ранее хранилище ключей сервера:

keytool -v -list -keystore serverkeystore.jks

Когда мы запускаем указанную выше команду, мы можем видеть детали хранилища ключей, в частности, владельца:

...
Owner: CN=localhost, OU=technology, O=example, L=city, ST=state, C=xx
...

CN владельца этого сертификата установлен на localhost. CN владельца должен точно соответствовать хосту сервера. Если есть какое-либо несоответствие, это приведет кSSLHandshakeException.

Давайте попробуем повторно сгенерировать сертификат сервера с CN как что-нибудь кроме localhost. Когда мы используем регенерированный сертификат сейчас для запускаSimpleServer иSimpleClient, он сразу же терпит неудачу:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
    java.security.cert.CertificateException:
    No name matching localhost found

Приведенная выше трассировка исключений ясно указывает на то, что клиент ожидал сертификат с именем localhost, который он не нашел.

Обратите внимание, чтоJSSE does not mandate hostname verification by default. Мы включили проверку имени хоста вSimpleClient посредством явного использования HTTPS:

SSLParameters sslParams = new SSLParameters();
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
((SSLSocket) connection).setSSLParameters(sslParams);

Проверка имени хоста является распространенной причиной сбоев и, как правило, всегда должна применяться для обеспечения большей безопасности. Дополнительные сведения о проверке имени хоста и ее важности для безопасности с помощью TLS см. Вthis article.

5.5. Несовместимая версия SSL

В настоящее время используются различные криптографические протоколы, в том числе разные версии SSL и TLS.

Как упоминалось ранее, SSL, в общем, был заменен TLS из-за своей криптографической стойкости. Криптографический протокол и версия являются дополнительным элементом, с которым клиент и сервер должны договориться во время рукопожатия.

Например, если сервер использует криптографический протокол SSL3, а клиент использует TLS1.3, они не могут договориться о криптографическом протоколе, и будет сгенерированSSLHandshakeException.

В нашемSimpleClient давайте изменим протокол на то, что несовместимо с протоколом, установленным для сервера:

((SSLSocket) connection).setEnabledProtocols(new String[] { "TLSv1.1" });

Когда мы снова запустим наш клиент, мы получимSSLHandshakeException:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
  No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

Трассировка исключений в таких случаях является абстрактной и не говорит нам точную проблему. To resolve these types of problems it is necessary to verify that both the client and server are using either the same or compatible cryptographic protocols.с

5.6. Несовместимый Cipher Suite

Клиент и сервер также должны договориться о наборе шифров, который они будут использовать для шифрования сообщений.

Во время рукопожатия клиент представит список возможных шифров для использования, и сервер ответит выбранным шифром из списка. Сервер сгенерируетSSLHandshakeException , если не сможет выбрать подходящий шифр.

В нашемSimpleClient давайте изменим набор шифров на что-то, несовместимое с набором шифров, используемым нашим сервером:

((SSLSocket) connection).setEnabledCipherSuites(
  new String[] { "TLS_RSA_WITH_AES_128_GCM_SHA256" });

Когда мы перезапустим наш клиент, мы получимSSLHandshakeException:

Exception in thread "main" javax.net.ssl.SSLHandshakeException:
  Received fatal alert: handshake_failure

Опять же, трассировка исключений довольно абстрактна и не говорит нам точную проблему. Решение этой ошибки заключается в проверке включенных комплектов шифров, используемых клиентом и сервером, и в наличии хотя бы одного общего комплекта.

Как правило, клиенты и серверы настроены на использование широкого набора наборов шифров, поэтому вероятность возникновения этой ошибки меньше. If we encounter this error it is typically because the server has been configured to use a very selective cipher. Сервер может выбрать принудительное применение выборочного набора шифров по соображениям безопасности.

6. Заключение

В этом уроке мы узнали о настройке SSL с использованием сокетов Java. Затем мы обсудили рукопожатия SSL с односторонним и двусторонним SSL. Наконец, мы рассмотрели список возможных причин, по которым рукопожатия SSL могут потерпеть неудачу, и обсудили решения.

This Post Covers javax.net.ssl.SSLHandshakeException PKIX path building failed error while running schedule job IDCS Group Lookup Reconciliation as a part of Oracle Identity Manager (OIM)/Oracle Identity Governance (OIG), Integration with Oracle Identity Cloud Service (IDCS).

Error: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target.

If you are new to Secure Socket Layer (SSL), then I would suggest you check our previous post where we have covered in detail.

  • SSL in WebLogic (CA, KeyStore, Identity & Trust Store): Things you must know – Part I Click Here
  • SSL in WebLogic Server – Part II : Create KeyStore, generate CSR, Import CERT and configure KeyStore with WebLogic Click Here

If you want to know more about Oracle Identity Cloud Service (IDCS) check our previous post where we have covered in detail about Oracle Identity Cloud Serve ice (IDCS) Overview & Concepts 

Issue: 

Oracle Identity Manager (OIM) was integrated with Identity Cloud Service (IDCS) using the OIM IDCS connector.

While Running schedule job IDCS Group Lookup Reconciliation fails with javax.net.ssl.SSLHandshakeException: PKIX path building failed error:

Log Filename = $DOMAIN_HOME/servers/oim_server1/logs/oim_server1-diagnostic.log.

————————————————–

Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1884)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1341)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:868)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:804)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339)

at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:290)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:259)
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:125)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:319)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:230)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:126)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1323)
… 155 more

Issue HandshakeException

Root Cause:

The OIM/OIG and Oracle Identity Cloud Service (IDCS) integration is performed only using an encrypted protocol (HTTPS). This type of communication requires that the Oracle Identity Cloud Service certificate is available in the OIM server trust store.

Whenever OIM Server attempts to connect to Oracle Identity Cloud Service (Target system),  it will only be able to connect to IDCS if it can trust it. The way trust is handled in the OIM Server is when you have Imported IDCS SSL Certificate in OIM and OIM JDK TrustStore Respectively.

OIM JDK TrustStore location: $JAVA_HOME/lib/security/cacerts

OIM Server TrustStore Location:  $WL_HOME/server/lib/DemoTrust.jks

This contains a list of all known Certificate Authority (CA) certificates, and OIM Server will only trust certificates that are signed by one of those CAs or public certificates that exist within that keystore.

Root Cause HandshakeException

These certificates combined are referred to as the certificate chain, and, as they are all within the OIM Server Truststore (DemoTrust.jks) and  OIM JDK Truststore (cacerts) OIM Server will trust any certificates signed by them (in this case, *.identoty.oraclecloud.com).

This problem is therefore caused by a certificate that is self-signed (a CA did not sign it) or a certificate chain that does not exist within the OIM Server TrustStore. OIM Server did not trust the certificate and fails to connect to the Oracle Identity Cloud Service (IDCS).

Troubleshooting:

You export the Oracle Identity Cloud Service HTTPS public certificate and import the same into the OIM trust store and make sure Enable SSL as “True” in IDCS IT Resource Parameter.

HighLevel Steps to export HTTP public certificate & Import them into OIM TrustStore:

  1. Export the HTTPS public certificate (IDCS Public certificate)
  2. Then, Check where to import OIM Trust Store
  3. After checking location, Import IDCS Public Certificate into OIM Trust Store
  4. Enable SSL as “True” in IDCS IT Resource Parameter
  5. Testing

1. How to Export the IDCS HTTPS public certificate:

In this section, you have to access the IDCS Service Console URL and export the HTTPs Public certificates.

  • Access the Oracle Identity Cloud Service UI in Firefox Browser
  • In the address bar, click the lock icon, click Connection>, and then click More Information.

Troubleshooting: Issue HandshakeException

  • Click View Certificate.

Troubleshooting: Issue HandshakeException

  • Click Details and then click Export

Troubleshooting: Issue HandshakeException

  • Select X.509 Certificate (PEM) as type and save the certificate file. Default name of this certificate is -identityoraclecloudcom , change it with idcs.cer
  • Transfer that certificate in the OIM Server under /tmp
  • Next, you need to import the IDCS certificates into the OIM trust store.

2. How to check Identity and Trust Keystore location into OIM

  • Login to WebLogic console http://<hostname>:7001/console and Go to -> Servers->OIM Managed Server (oim_server1)

Keystore Location

  • Click on Configuration->keystores (This page let you view and define Identity & Trust keystore configurations)

Keystore configuration

3. How to import the IDCS HTTPS public certificate into the OIM trust store: 

After exporting the IDCS HTTPs Public Certificates, you have to import them into the OIM Server and OIM JDK trust store.

  • Launch the OIM server terminal and enter the following command to import the certificate into the OIM JDK trust store. (use root user else you will hit error permission denied at the end )

keytool -import -keystore $JAVA_HOME/jre/lib/security/cacerts -file /tmp/idcs.cer -storepass changeit -alias idcs

Troubleshooting: Issue HandshakeException

Click yesThe keytool utility should display the message Certificate was added to keystore.

  • Now, import the certificate into the OIM Server trust store:

keytool -import -keystore $WL_HOME/server/lib/DemoTrust.jks -file /tmp/idcs.cer -storepass DemoTrustKeyStorePassPhrase -alias idcsKeytool

  • When prompted, enter yes.

HandshakeException

The keytool utility should display the message Certificate was added to keystore.

4.  How to Enable SSL as “True” in IDCS IT Resource Parameter

IDCS always expects SSL communication, so sslEnabled must be set on “true” in the IDCS IT Resource.

  • login to Identity System Administration Console http://<hostname>:14000/syadmin and Go to IT Resource -> Edit -> IDCS IT resource

SSL as "True" in IDCS IT Resource Parameter

  • Edit IT Resource Details and Parameters and add sslEnables as “True

HandshakeException

  • View IT Resource Details and Parameters and Confirm details

HandshakeException

Testing:

We could successfully run the Scheduled jobs in OIM/OIG.

Testing: HandshakeException

This post is from our Oracle Identity Cloud Service (IDCS) training in which we have covered everything one should know about Oracle Identity Cloud Service like ArchitectureUsers & Groups Application, Branding, Auditing & Reports, Rest APIs, Adaptive security and much more.

Related Posts

  • Cloud Security With Oracle Identity Cloud Service (IDCS) Click here
  • Oracle Identity Cloud Service (IDCS): Step By Step Lab Activity Guide for Beginners Click here
  • Oracle Identity Cloud Service (IDCS) Overview & Concepts Click here
  • Cloud Security Using Identity Cloud Service Click here
  • Oracle Identity Cloud Service: Configuring Multi-Factor Authentication (MFA) Click here
  • High-level Steps to Protecting an On-premises Application with IDCS by Using WebGate Click Here
  • OIM/OIG – IDCS Connector Integration : [Troubleshooting] InvalidCredentialException: HTTP 401 Error : User not authorized to execute service Click here

References

  • Oracle Identity Cloud Service: Integrating with Oracle Identity Manager (OIM)
  • SSL in WebLogic (CA, KeyStore, Identity & Trust Store): Things you must know – Part I Click Here
  • SSL in WebLogic Server – Part II: Create KeyStore, generate CSR, Import CERT and configure KeyStore with WebLogic Click Here

Next Task for You

Download our FREE Guide i.e., 7 Docs Must Read to Become Expert in Cloud Security With Oracle Identity Cloud Service (IDCS).

Click on the image below to Download the Guide:

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ятрогенная патология врачебные ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Ошибка javascript при запуске компьютера