Принять самозаверяющий ssl-сертификат сервера в Java-клиенте
Это выглядит как стандартный вопрос, но я нигде не смог найти четких указаний.
У меня есть Java-код, пытающийся подключиться к серверу с, вероятно, самозаверяющим (или истекшим) сертификатом. Код сообщает о следующей ошибке :
[HttpMethodDirector] I/O exception (javax.net.ssl.SSLHandshakeException) caught
when processing request: sun.security.validator.ValidatorException: PKIX path
building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
Насколько я понимаю, я должен использовать keytool и сообщить java, что разрешить это соединение можно.
Все инструкции по устранению этой проблемы предполагают, что я полностью владею keytool, например
сгенерируйте закрытый ключ для сервера и импортируйте его в хранилище ключей
Есть ли кто-нибудь, кто мог бы опубликовать подробные инструкции?
Я использую unix, поэтому лучше всего подойдет bash-скрипт.
Не уверен, важно ли это, но код выполняется в jboss.
Переведено автоматически
Ответ 1
Здесь у вас есть в основном два варианта: добавить самозаверяющий сертификат в ваш JVM truststore или настроить ваш клиент на
Вариант 1
Экспортируйте сертификат из своего браузера и импортируйте его в свой JVM truststore (для установления цепочки доверия):
<JAVA_HOME>\bin\keytool -import -v -trustcacerts
-alias server-alias -file server.cer
-keystore cacerts.jks -keypass changeit
-storepass changeit
Вариант 2
Отключить проверку сертификата (код из хранилища примеров):
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (GeneralSecurityException e) {
}
// Now you can access an https URL without having the certificate in the truststore
try {
URL url = new URL("https://hostname/index.html");
} catch (MalformedURLException e) {
}
Обратите внимание, что я вообще не рекомендую вариант № 2. Отключение менеджера доверия приводит к повреждению некоторых частей SSL и делает вас уязвимыми для атак "человек посередине". Предпочтите вариант № 1 или, что еще лучше, попросите сервер использовать "настоящий" сертификат, подписанный хорошо известным центром сертификации.
Ответ 2
Есть лучшая альтернатива доверять всем сертификатам: создайте TrustStore
который конкретно доверяет данному сертификату и используйте это для создания SSLContext
из которого можно получить SSLSocketFactory
для установки на HttpsURLConnection
. Вот полный код:
File crtFile = new File("server.crt");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));
// Or if the crt-file is packaged into a jar file:
// CertificateFactory.getInstance("X.509").generateCertificate(this.class.getClassLoader().getResourceAsStream("server.crt"));
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("server", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
В качестве альтернативы вы можете загрузить KeyStore
непосредственно из файла или получить сертификат X.509 из любого надежного источника.
Обратите внимание, что с этим кодом сертификаты из cacerts
использоваться не будут. Этот конкретный HttpsURLConnection
будет доверять только этому конкретному сертификату.
Ответ 3
Apache HttpClient 4.5 поддерживает прием самозаверяющих сертификатов:
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(new TrustSelfSignedStrategy())
.build();
SSLConnectionSocketFactory socketFactory =
new SSLConnectionSocketFactory(sslContext);
Registry<ConnectionSocketFactory> reg =
RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", socketFactory)
.build();
HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse sslResponse = httpClient.execute(httpGet);
Это создает фабрику SSL-сокетов, которая будет использовать TrustSelfSignedStrategy
, регистрирует ее в пользовательском диспетчере подключений, а затем выполняет HTTP GET с использованием этого менеджера подключений.
Я согласен с теми, кто скандирует "не делайте этого в production", однако существуют варианты использования для принятия самозаверяющих сертификатов вне production; мы используем их в автоматизированных интеграционных тестах, так что мы используем SSL (как в production), даже когда не работаем на производственном оборудовании.
Ответ 4
Я обратился с этой проблемой к поставщику сертификатов, который по состоянию на JDK 8u74
не является частью доверенных хостов JVM по умолчанию. Провайдер - это www.identrust.com, но это был не тот домен, к которому я пытался подключиться. Этот домен получил свой сертификат от этого провайдера. Смотрите, будет ли перекрестный root покрывать доверие списком по умолчанию в JDK / JRE? -- прочитайте пару записей. Также смотрите, какие браузеры и операционные системы поддерживают Let's Encrypt.
Итак, чтобы подключиться к интересующему меня домену, от которого был выдан сертификат, identrust.com
я выполнил следующие шаги. По сути, мне нужно было получить сертификат identrust.com (DST Root CA X3
), которому JVM будет доверять. Я смог сделать это с помощью Apache HttpComponents 4.5 примерно так:
1. Получите сертификат от indettrust в Инструкции по загрузке цепочки сертификатов. Нажмите на ссылку DST Root CA X3.
2: Сохраните строку в файл с именем "DST Root CA X3.pem". Обязательно добавьте строки "-----BEGIN CERTIFICATE-----" и "-----END CERTIFICATE----" в файл в начале и конце.
3. Создайте файл хранилища ключей java, cacerts.jks, с помощью следующей команды:
keytool -import -v -trustcacerts -alias IdenTrust -keypass yourpassword -file dst_root_ca_x3.pem -keystore cacerts.jks -storepass yourpassword
4. Скопируйте результирующее хранилище ключей cacerts.jks в каталог ресурсов вашего java / (maven) приложения.
5. Используйте следующий код, чтобы загрузить этот файл и прикрепить его к Apache 4.5 HttpClient. Это решит проблему для всех доменов, у которых есть сертификаты, выданные от indetrust.com
util oracle включает сертификат в хранилище ключей JRE по умолчанию.
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File(CalRestClient.class.getResource("/cacerts.jks").getFile()), "yourpasword".toCharArray(),
new TrustSelfSignedStrategy())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
Когда проект будет собран, cacerts.jks будет скопирован в classpath и загружен оттуда. На данный момент я не тестировал другие ssl-сайты, но если приведенный выше код "соединится" в этом сертификате, то они тоже будут работать, но опять же, я не знаю.
Ссылка: Пользовательский контекст SSL и Как мне принять самозаверяющий сертификат с помощью Java HttpsURLConnection?