Игнорирование SSL-сертификата в Apache HttpClient 4.3
Как игнорировать сертификат SSL (доверять всем) для Apache HttpClient 4.3?
все ответы, которые я нашел на так относиться к предыдущим версиям, и API изменился.
по теме:
- как игнорировать ошибки SSL сертификата в Apache HttpClient 4.0
- как обрабатывать недействительные SSL-сертификаты с помощью Apache HttpClient?
- нужно доверять всем сертификатам в течение разработка с использованием пружины
- игнорировать ошибки SSL сертификата с Java
Edit:
- это только для тестовых целей. Дети, не пытайтесь сделать это дома (или на производстве)
14 ответов:
приведенный ниже код работает для доверительных самозаверяющих сертификатов. Вы должны использовать TrustSelfSignedStrategy при создании своего клиента:
SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( builder.build()); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).build(); HttpGet httpGet = new HttpGet("https://some-server"); CloseableHttpResponse response = httpclient.execute(httpGet); try { System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } finally { response.close(); }
Я не включил
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
нарочно: смысл состоял в том, чтобы разрешить тестирование с самозаверяющими сертификатами, поэтому вам не нужно приобретать соответствующий сертификат у центра сертификации. Вы можете легко создать самозаверяющий сертификат с правильным именем хоста, так что вместо добавленияSSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
флаг.
Если вы используете процедуру PoolingHttpClientConnectionManager выше не работает, пользовательский SSLContext игнорируется. Вы должны передать socketFactoryRegistry в contructor при создании PoolingHttpClientConnectionManager.
SSLContextBuilder builder = SSLContexts.custom(); builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); SSLContext sslContext = builder.build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslContext, new X509HostnameVerifier() { @Override public void verify(String host, SSLSocket ssl) throws IOException { } @Override public void verify(String host, X509Certificate cert) throws SSLException { } @Override public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { } @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder .<ConnectionSocketFactory> create().register("https", sslsf) .build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager( socketFactoryRegistry); CloseableHttpClient httpclient = HttpClients.custom() .setConnectionManager(cm).build();
в качестве дополнения к ответу @mavroprovato, если вы хотите доверять всем сертификатам вместо того, чтобы просто самозаверять, вы бы сделали (в стиле вашего кода)
builder.loadTrustMaterial(null, new TrustStrategy(){ public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } });
или (прямое копирование-вставка из моего собственного кода):
import javax.net.ssl.SSLContext; import org.apache.http.ssl.TrustStrategy; import org.apache.http.ssl.SSLContexts; // ... SSLContext sslContext = SSLContexts .custom() //FIXME to contain real trust store .loadTrustMaterial(new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }) .build();
и если вы хотите пропустить проверку имени хоста, вам нужно установить
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).setSSLHostnameVerifier( NoopHostnameVerifier.INSTANCE).build();
как хорошо. (ALLOW_ALL_HOSTNAME_VERIFIER является устаревшим).
обязательное предупреждение: вы не должны действительно сделать это, принимая все сертификаты-это плохо. Однако есть некоторые редкие случаи использования, когда вы хотите это сделать.
как примечание к ранее приведенному коду, вы захотите закрыть ответ, даже если httpclient.execute () выдает исключение
CloseableHttpResponse response = null; try { response = httpclient.execute(httpGet); System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } finally { if (response != null) { response.close(); } }
код выше был протестирован с помощью
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.3</version> </dependency>
и для заинтересованных, вот мой полный набор тестов:
import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.TrustStrategy; import org.apache.http.util.EntityUtils; import org.junit.Test; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; public class TrustAllCertificatesTest { final String expiredCertSite = "https://expired.badssl.com/"; final String selfSignedCertSite = "https://self-signed.badssl.com/"; final String wrongHostCertSite = "https://wrong.host.badssl.com/"; static final TrustStrategy trustSelfSignedStrategy = new TrustSelfSignedStrategy(); static final TrustStrategy trustAllStrategy = new TrustStrategy(){ public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }; @Test public void testSelfSignedOnSelfSignedUsingCode() throws Exception { doGet(selfSignedCertSite, trustSelfSignedStrategy); } @Test(expected = SSLHandshakeException.class) public void testExpiredOnSelfSignedUsingCode() throws Exception { doGet(expiredCertSite, trustSelfSignedStrategy); } @Test(expected = SSLPeerUnverifiedException.class) public void testWrongHostOnSelfSignedUsingCode() throws Exception { doGet(wrongHostCertSite, trustSelfSignedStrategy); } @Test public void testSelfSignedOnTrustAllUsingCode() throws Exception { doGet(selfSignedCertSite, trustAllStrategy); } @Test public void testExpiredOnTrustAllUsingCode() throws Exception { doGet(expiredCertSite, trustAllStrategy); } @Test(expected = SSLPeerUnverifiedException.class) public void testWrongHostOnTrustAllUsingCode() throws Exception { doGet(wrongHostCertSite, trustAllStrategy); } @Test public void testSelfSignedOnAllowAllUsingCode() throws Exception { doGet(selfSignedCertSite, trustAllStrategy, NoopHostnameVerifier.INSTANCE); } @Test public void testExpiredOnAllowAllUsingCode() throws Exception { doGet(expiredCertSite, trustAllStrategy, NoopHostnameVerifier.INSTANCE); } @Test public void testWrongHostOnAllowAllUsingCode() throws Exception { doGet(expiredCertSite, trustAllStrategy, NoopHostnameVerifier.INSTANCE); } public void doGet(String url, TrustStrategy trustStrategy, HostnameVerifier hostnameVerifier) throws Exception { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(trustStrategy); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( builder.build()); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).setSSLHostnameVerifier(hostnameVerifier).build(); HttpGet httpGet = new HttpGet(url); CloseableHttpResponse response = httpclient.execute(httpGet); try { System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } finally { response.close(); } } public void doGet(String url, TrustStrategy trustStrategy) throws Exception { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(trustStrategy); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( builder.build()); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).build(); HttpGet httpGet = new HttpGet(url); CloseableHttpResponse response = httpclient.execute(httpGet); try { System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } finally { response.close(); } } }
(рабочий тестовый проект в github)
одно небольшое дополнение к ответу васекта:
предоставленное решение с SocketFactoryRegistry работает при использовании PoolingHttpClientConnectionManager.
однако соединения через простой http больше не работают. Вы должны добавить PlainConnectionSocketFactory для протокола http дополнительно, чтобы заставить их работать снова:
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create() .register("https", sslsf) .register("http", new PlainConnectionSocketFactory()).build();
после попытки различных вариантов, следующая конфигурация работала как для http, так и для https
SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(),SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", new PlainConnectionSocketFactory()) .register("https", sslsf) .build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry); cm.setMaxTotal(2000);//max connection //System.setProperty("jsse.enableSNIExtension", "false"); //"" CloseableHttpClient httpClient = HttpClients.custom() .setSSLSocketFactory(sslsf) .setConnectionManager(cm) .build();
Я использую http-клиент 4.3.3 -
compile 'org.apache.httpcomponents:httpclient:4.3.3'
более простой и короткий рабочий код:
мы используем HTTPClient 4.3.5 и мы пробовали почти все решения существуют на stackoverflow но ничего, Подумав и выяснив проблему, мы приходим к следующему коду, который отлично работает, просто добавьте его перед созданием экземпляра класса HttpClient.
какой-то метод, который вы используете, чтобы сделать запрос POST...
SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(builder.build(), SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslSF).build(); HttpPost postRequest = new HttpPost(url);
продолжить вызов и использование экземпляра HttpPost в нормальная форма
при использовании http-клиента 4.5 мне пришлось использовать javasx.net.ssl.HostnameVerifier, чтобы разрешить любое имя хоста (для целей тестирования). Вот что я в конечном итоге сделал:
CloseableHttpClient httpClient = null; try { SSLContextBuilder sslContextBuilder = new SSLContextBuilder(); sslContextBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); HostnameVerifier hostnameVerifierAllowAll = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build(), hostnameVerifierAllowAll); CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( new AuthScope("192.168.30.34", 8443), new UsernamePasswordCredentials("root", "password")); httpClient = HttpClients.custom() .setSSLSocketFactory(sslSocketFactory) .setDefaultCredentialsProvider(credsProvider) .build(); HttpGet httpGet = new HttpGet("https://192.168.30.34:8443/axis/services/getStuff?firstResult=0&maxResults=1000"); CloseableHttpResponse response = httpClient.execute(httpGet); int httpStatus = response.getStatusLine().getStatusCode(); if (httpStatus >= 200 && httpStatus < 300) { [...] } else { throw new ClientProtocolException("Unexpected response status: " + httpStatus); } } catch (Exception ex) { ex.printStackTrace(); } finally { try { httpClient.close(); } catch (IOException ex) { logger.error("Error while closing the HTTP client: ", ex); } }
вот рабочая перегонка вышеуказанных методов, эквивалентная "curl-insecure":
HttpClient getInsecureHttpClient() throws GeneralSecurityException { TrustStrategy trustStrategy = new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) { return true; } }; HostnameVerifier hostnameVerifier = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }; return HttpClients.custom() .setSSLSocketFactory(new SSLConnectionSocketFactory( new SSLContextBuilder().loadTrustMaterial(trustStrategy).build(), hostnameVerifier)) .build(); }
на
PoolingHttpClientConnectionManager
вместе сRegistry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create().register("https", sslFactory).build();
Если вы хотите асинхронный httpclient с помощьюPoolingNHttpClientConnectionManager
код должен быть похож на следующееSSLContextBuilder builder = SSLContexts.custom(); builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); SSLContext sslContext = builder.build(); SchemeIOSessionStrategy sslioSessionStrategy = new SSLIOSessionStrategy(sslContext, new HostnameVerifier(){ @Override public boolean verify(String hostname, SSLSession session) { return true;// TODO as of now allow all hostnames } }); Registry<SchemeIOSessionStrategy> sslioSessionRegistry = RegistryBuilder.<SchemeIOSessionStrategy>create().register("https", sslioSessionStrategy).build(); PoolingNHttpClientConnectionManager ncm = new PoolingNHttpClientConnectionManager(new DefaultConnectingIOReactor(),sslioSessionRegistry); CloseableHttpAsyncClient asyncHttpClient = HttpAsyncClients.custom().setConnectionManager(ncm).build(); asyncHttpClient.start();
class ApacheHttpClient { /*** * This is a https get request that bypasses certificate checking and hostname verifier. * It uses basis authentication method. * It is tested with Apache httpclient-4.4. * It dumps the contents of a https page on the console output. * It is very similar to http get request, but with the additional customization of * - credential provider, and * - SSLConnectionSocketFactory to bypass certification checking and hostname verifier. * @param path String * @param username String * @param password String * @throws IOException */ public void get(String path, String username, String password) throws IOException { final CloseableHttpClient httpClient = HttpClients.custom() .setDefaultCredentialsProvider(createCredsProvider(username, password)) .setSSLSocketFactory(createGenerousSSLSocketFactory()) .build(); final CloseableHttpResponse response = httpClient.execute(new HttpGet(path)); try { HttpEntity entity = response.getEntity(); if (entity == null) return; System.out.println(EntityUtils.toString(entity)); } finally { response.close(); httpClient.close(); } } private CredentialsProvider createCredsProvider(String username, String password) { CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials(username, password)); return credsProvider; } /*** * * @return SSLConnectionSocketFactory that bypass certificate check and bypass HostnameVerifier */ private SSLConnectionSocketFactory createGenerousSSLSocketFactory() { SSLContext sslContext; try { sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, new TrustManager[]{createGenerousTrustManager()}, new SecureRandom()); } catch (KeyManagementException | NoSuchAlgorithmException e) { e.printStackTrace(); return null; } return new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); } private X509TrustManager createGenerousTrustManager() { return new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] cert, String s) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] cert, String s) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; } }
(Я бы добавил комментарий непосредственно к ответу васекта, но у меня недостаточно очков репутации (не уверен, что логика там)
в любом случае... я хотел сказать, что даже если вы явно не создаете / не просите PoolingConnection, это не значит, что вы его не получаете.
Я сходил с ума, пытаясь понять, почему оригинальное решение не сработало для меня, но я проигнорировал ответ васекта, поскольку он "не применялся к моему делу" - неправильно!
Я глядя на мой стек-след, когда низко и вот я увидел соединение пула в середине его. Бац-я устал от его сложения и успеха!! (демо завтра, и я был в отчаянии) :-)
доверять всем сертификатам в Apache HTTP Client
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) { } } }; try { SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sc); httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).build(); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
вы можете использовать следующий фрагмент кода для получения экземпляра HttpClient без проверки сертификации ssl.
private HttpClient getSSLHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { LogLoader.serverLog.trace("In getSSLHttpClient()"); SSLContext context = SSLContext.getInstance("SSL"); TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } }; context.init(null, new TrustManager[] { tm }, null); HttpClientBuilder builder = HttpClientBuilder.create(); SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(context); builder.setSSLSocketFactory(sslConnectionFactory); PlainConnectionSocketFactory plainConnectionSocketFactory = new PlainConnectionSocketFactory(); Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("https", sslConnectionFactory).register("http", plainConnectionSocketFactory).build(); PoolingHttpClientConnectionManager ccm = new PoolingHttpClientConnectionManager(registry); ccm.setMaxTotal(BaseConstant.CONNECTION_POOL_SIZE); ccm.setDefaultMaxPerRoute(BaseConstant.CONNECTION_POOL_SIZE); builder.setConnectionManager((HttpClientConnectionManager) ccm); builder.disableRedirectHandling(); LogLoader.serverLog.trace("Out getSSLHttpClient()"); return builder.build(); }
Если вы используете
HttpClient 4.5.x
ваш код может быть следующего вида:SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, TrustSelfSignedStrategy.INSTANCE).build(); SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory( sslContext, NoopHostnameVerifier.INSTANCE); HttpClient httpClient = HttpClients.custom() .setDefaultCookieStore(new BasicCookieStore()) .setSSLSocketFactory(sslSocketFactory) .build();