SSLHandshakeException появляются в журналах, когда происходит какая-то ошибка при проверке сертификата, установленного на клиентской машине, с сертификатом на серверной машине. В этой статье мы узнаем, как исправить это, если вы используете библиотеку Apache HttpClient для создания HttpClient для подключения к URL-адресам, защищенным SSL/TLS.
1. Проблема
Журналы исключений будут выглядеть следующим образом.
Причина: javax.net.ssl.SSLHandshakeException: Удаленный хост закрыл соединение во время установления связив sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:980)......Причина: java.io.EOFException: SSL-пир завершил работу некорректнов sun.security.ssl.InputRecord.read(InputRecord.java:505)......
Я уже опубликовал исправление кода для обхода сопоставления SSL в предыдущем посте.
К сожалению, это исправление работает в протоколах TLS и TLS 1.1. Оно не работает в протоколе TLS 1.2. Так что в конечном итоге вам в любом случае нужно исправить проблему с сертификатом. Для этого есть исправление «только без кода».
2. Решение
Теперь есть два способа, вы можете использовать импортированный сертификат с сервера. Либо добавить сертификат в хранилище JDK cacerts; либо передать информацию о сертификате в аргументах JVM.
2.1. Импорт сертификата в JDK cacert Store
Импортируйте сертификат с сервера.
Используйте данную команду для добавления сертификата в хранилище JDK.(Удалите символы новой строки).
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
Теперь создайте HttpClient, как указано ниже:
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;}
Обратите внимание на код: SSLContext.getInstance(«TLSv1.2»). Этот код выбирает сертификаты, добавленные в хранилище JDK cacert. Так что запомните его.
2.2. Передача информации о сертификате в аргументах JVM
Импортируйте сертификат с сервера.
Добавьте аргументы JVM при запуске сервера. Измените значения параметров в соответствии с вашим приложением.
-Djavax.net.ssl.keyStore="C:/Users/Lokesh\keys\maven.jks"-Djavax.net.ssl.keyStorePassword="test"-Djavax.net.ssl.trustStore="C:/Users/Lokesh\keys\maven.jks"-Djavax.net.ssl.trustStorePassword="test"
Теперь создайте HTTP-клиент, как указано ниже:
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;}
Обратите внимание на код: SSLContext.createSystemDefault(). Этот код выбирает сертификаты, переданные как аргументы JVM. Опять же, запишите его.
3. Заключение
- Используйте SSLContext.getInstance(«TLSv1.2») при добавлении сертификата в хранилище JDK cacert.
- Используйте SSLContext.createSystemDefault(), когда информация SSL передается как аргумент JVM.
Пишите мне свои вопросы в комментариях.