В зависимости от конкретной проблемы с сертификатом, вы можете получить сообщение об ошибке, подобное одному из следующих, хотя я почти уверен, что есть и другие проявления:
java.security.cert.CertificateException: Untrusted Server Certificate Chain 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
Чтобы обойти это, часто требуется изменить файлы хранилища доверенных сертификатов JDK, что может быть болезненным, и часто вы сталкиваетесь с проблемами. Кроме того, каждый разработчик в вашей команде должен будет делать то же самое, и в каждой новой среде у вас будут повторяться одни и те же проблемы.
К счастью, есть способ справиться с проблемой общим способом, который не будет обременять ваших разработчиков. Мы собираемся сосредоточиться на ванильных соединениях типа HttpURLConnection, поскольку они являются наиболее общими и все же должны помочь вам понять направление, которое следует использовать с другими библиотеками. Если вы используете Apache HttpClient, смотрите здесь .
Предупреждение: знайте, что вы делаете!
Имейте в виду, что означает использование этого кода: это означает, что вам вообще не нужно проверять хост и вы используете SSL только для шифрования сообщений. Вы не предотвращаете атаки типа «человек посередине» и не проверяете, что вы подключены к тому хосту, которым себя считаете. Обычно это сводится к нескольким действительным случаям:
- вы работаете в закрытой локальной сети. Вы не подвержены перехвату ваших запросов злоумышленником (или, если это так, у вас есть большие проблемы).
- Вы находитесь в среде тестирования или разработки, где защита связи не важна.
Если это соответствует вашим потребностям, тогда продолжайте. В противном случае, возможно, подумайте дважды, что вы пытаетесь достичь.
Решение: изменение доверительных управляющих
Теперь, когда мы прошли этот отказ от ответственности, мы можем решить актуальную проблему под рукой. Java позволяет нам управлять объектами, ответственными за проверку хоста и сертификата для HttpsURLConnection. Это может быть сделано в глобальном масштабе, но я уверен, что те из вас, кто имеет опыт, будут испытывать страх перед мыслью о столь радикальном изменении. К счастью, мы также можем сделать это для каждого запроса, и так как примеры этого трудно найти в Интернете, я предоставил код ниже. Этот подход хорош, поскольку вам не нужно возиться с заменой реализаций SSLSocketFactory на глобальном уровне.
Не стесняйтесь захватить это и использовать это в своем проекте.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package com.mycompany.http;import java.net.*;import javax.net.ssl.*;import java.security.*;import java.security.cert.*;public class TrustModifier { private static final TrustingHostnameVerifier TRUSTING_HOSTNAME_VERIFIER = new TrustingHostnameVerifier(); private static SSLSocketFactory factory; /** Call this with any HttpURLConnection, and it will modify the trust settings if it is an HTTPS connection. */ public static void relaxHostChecking(HttpURLConnection conn) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { if (conn instanceof HttpsURLConnection) { HttpsURLConnection httpsConnection = (HttpsURLConnection) conn; SSLSocketFactory factory = prepFactory(httpsConnection); httpsConnection.setSSLSocketFactory(factory); httpsConnection.setHostnameVerifier(TRUSTING_HOSTNAME_VERIFIER); } } static synchronized SSLSocketFactory prepFactory(HttpsURLConnection httpsConnection) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { if (factory == null) { SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, new TrustManager[]{ new AlwaysTrustManager() }, null); factory = ctx.getSocketFactory(); } return factory; } private static final class TrustingHostnameVerifier implements HostnameVerifier { public boolean verify(String hostname, SSLSession session) { return true; } } private static class AlwaysTrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }} |
использование
Чтобы использовать приведенный выше код, просто вызовите метод relaxHostChecking (), прежде чем открывать поток:
|
1
2
3
4
5
6
7
8
9
|
URL someUrl = ... // may be HTTPS or HTTPHttpURLConnection connection = (HttpURLConnection) someUrl.openConnection();TrustModifier.relaxHostChecking(connection); // here's where the magic happens// Now do your work!// This connection will now live happily with expired or self-signed certificatesconnection.setDoOutput(true);OutputStream out = connection.getOutputStream();... |
Вот вам и полный пример локализованного подхода к поддержке самозаверяющих сертификатов. Это не влияет на остальную часть вашего приложения, которое по-прежнему будет иметь строгую семантику проверки хостинга. Этот пример можно расширить, чтобы использовать параметр конфигурации, чтобы определить, следует ли использовать упрощенную проверку хоста, и я рекомендую вам сделать это, если использование этого кода является главным способом облегчения разработки с использованием самозаверяющих сертификатов.
Ссылка: Игнорирование самоподписанных сертификатов на Java от наших партнеров по JCG в Carfey Software Blog .
- Защита веб-службы RESTful с помощью Spring Security 3.1
- Защита приложений GWT с помощью Spring Security
- JBoss 4.2.x Spring 3 JPA Учебное пособие по Hibernate
- Отладка производственного сервера — демонстрация Eclipse и JBoss
- Java EE6 CDI, именованные компоненты и классификаторы
- Список учебных пособий по Java и Android