Вопрос-ответ

Accept server's self-signed ssl certificate in Java client

Принять самозаверяющий 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?

java