Проблема, с которой часто сталкиваются начинающие разработчики Java, заключается в том, что при использовании HttpClient
для подключения к серверу по HTTPS, они получают исключение javax.net.ssl.SSLException: Not trusted server certificate
. Это означает, что клиент не доверяет сертификату сервера.
Приведем пример кода, в котором возникает данная проблема:
public void connect() throws Exception { HttpPost post = new HttpPost(new URI("https://example.com")); post.setEntity(new StringEntity("body")); KeyStore trusted = KeyStore.getInstance("BKS"); trusted.load(null, "".toCharArray()); SSLSocketFactory sslf = new SSLSocketFactory(trusted); sslf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("https", sslf, 443)); SingleClientConnManager cm = new SingleClientConnManager(post.getParams(), schemeRegistry); HttpClient client = new DefaultHttpClient(cm, post.getParams()); HttpResponse result = client.execute(post); }
В этом коде мы создаем HTTP-клиент, который пытается подключиться к серверу по HTTPS и отправить ему POST-запрос. Мы используем SSLSocketFactory
с пустым хранилищем ключей (KeyStore
), что означает, что клиент не доверяет ни одному сертификату. Мы также указываем, что клиент должен доверять всем хостам, установив HostnameVerifier
в ALLOW_ALL_HOSTNAME_VERIFIER
.
Однако, когда мы пытаемся выполнить запрос, мы получаем исключение SSLException
.
Решение
Проблема заключается в том, что, хотя мы и указываем, что хотим доверять всем хостам, мы не доверяем ни одному сертификату. Для решения этой проблемы мы можем создать TrustManager
, который доверяет всем сертификатам.
public void connect() throws Exception { TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) { } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) { } } }; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); // Now we can proceed as before HttpPost post = new HttpPost(new URI("https://example.com")); post.setEntity(new StringEntity("body")); HttpClient client = new DefaultHttpClient(); HttpResponse result = client.execute(post); }
В этом коде мы создаем TrustManager
, который не проверяет сертификаты. Затем мы используем его для создания SSLContext
, который мы устанавливаем в качестве стандартной SSLSocketFactory
для HttpsURLConnection
. Теперь мы можем использовать HttpClient
без получения исключения SSLException
.
Важно отметить, что этот подход имеет свои риски и должен использоваться только в тех случаях, когда вы абсолютно уверены, что подключаетесь к надежному серверу. Доверяя всем сертификатам, вы открываете возможность для так называемых «man-in-the-middle» атак, когда злоумышленник может перехватить и возможно изменить ваше зашифрованное соединение.
Добавить комментарий