在Apache HttpClient 4.3中忽略SSL证书

如何忽略Apache HttpClient 4.3的 SSL证书(全部信任)?

我在SO上发现的所有答案都对待以前的版本,并且API已经改变。

有关:

  • 如何忽略Apache HttpClient 4.0中的SSL证书错误
  • 如何使用Apache HttpClient处理无效的SSL证书?
  • 在开发过程中需要使用Spring来信任所有的证书
  • 使用Java忽略SSL证书错误

编辑:

  • 这只是为了testing目的。 孩子们,不要在家里(或在生产)

下面的代码适用于信任自签名证书。 创build客户端时必须使用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 :关键是允许使用自签名证书进行testing,因此您不必从证书颁发机构获取适当的证书。 您可以使用正确的主机名轻松创build一个自签名证书,所以不用添加SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER标志。

如果使用上面的PoolingHttpClientConnectionManager过程不起作用,则自定义的SSLContext将被忽略。 创buildPoolingHttpClientConnectionManager时必须在构造函数中传递socketFactoryRegistry。

 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(); 

vasekt的一个小补充:

当使用PoolingHttpClientConnectionManager时,提供的解决scheme与SocketFactoryRegistry一起工作。

但是,通过纯http的连接不再工作。 您必须另外为http协议添加一个PlainConnectionSocketFactory,以使它们再次工作:

 Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create() .register("https", sslsf) .register("http", new PlainConnectionSocketFactory()).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(); 

如果你想跳过主机名validation,你需要设置

  CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).setSSLHostnameVerifier( NoopHostnameVerifier.INSTANCE).build(); 

以及。 (ALLOW_ALL_HOSTNAME_VERIFIER已弃用)。

强制性警告:你不应该这样做,接受所有证书是一件坏事。 但是,有一些罕见的情况下,你想这样做。

作为先前给出的代码的注释,即使httpclient.execute()抛出exception,也要closures响应

 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(); } } 

上面的代码是使用testing

 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.3</version> </dependency> 

而对于感兴趣的,这是我的全套testing集:

 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工作testing项目)

尝试各种选项后,以下configuration为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-client 4.3.3 –

compile 'org.apache.httpcomponents:httpclient:4.3.3'

更简单和更短的工作代码:

我们使用的HTTPClient 4.3.5,我们尝试了几乎所有的解决scheme存在于stackoverflow但没有什么,在思考和找出问题后,我们来到下面的代码,完美的作品,只是添加它之前创buildHttpClient实例。

您使用的一些方法,使发布请求…

 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); 

继续调用并以正常forms使用HttpPost实例

当使用http客户端4.5时,我必须使用javasx.net.ssl.HostnameVerifier来允许任何主机名(用于testing目的)。 这是我最终做的:

 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 – disincure”:

 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(); } 
 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; } }; } } 

PoolingHttpClientConnectionManager之上,还有Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create().register("https", sslFactory).build(); 如果你想要一个使用PoolingNHttpClientConnectionManager的asynchronoushttpclient,代码应该和下面类似

 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(); 

(我会直接添加评论vasekt的答案,但我没有足够的声誉点(不知道那里的逻辑)

无论如何…我想说的是,即使你没有明确地创build/要求一个PoolingConnection,并不意味着你没有得到一个。

我疯了,试图找出原来的解决scheme为什么不适合我,但我忽略了vasekt的答案,因为它“不适用于我的情况” – 错了!

我低盯着堆栈的踪迹,看到中间有一个PoolingConnection。 砰 – 我厌倦了他的join和成功! (我们的演示是明天,我越来越绝望):-)

信任Apache HTTP客户端中的所有Certs

 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());