1.问题背景
使用HttpClient访问某些Https协议的接口时,会抛出如下异常。
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
原因是接口提供方所使用的SSL证书不在JDK的可信证书列表中。
2.解决方案
2.1 思路概要
使用 SSLConnectionSocketFactory 跳过SSL证书认证。
2.2 处理步骤
本文以HttpClient4.5.3为例。
1)定义一个HttpClientBuilder,用于设置 CloseableHttpClient 的配置信息
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); |
2)定义一个SSLConnectionSocketFactory,用于跳过SSL证书认证。
SSLContext ctx = SSLContext.getInstance("TLS"); // 创建一个上下文(此处指定的协议类型似乎不是重点) X509TrustManager tm = new X509TrustManager() { // 创建一个跳过SSL证书的策略 public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } }; ctx.init(null, new TrustManager[] { tm }, null); // 使用上面的策略初始化上下文 SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(ctx, new String[] { "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" }, null, NoopHostnameVerifier.INSTANCE); // 创建一个包含各种SSL协议的SSLConnectionSocketFactory实例对象 |
3)生产HttpClient实例
httpClientBuilder.setSSLSocketFactory(ssf ); // 设置SSL管理工厂 ... // 设置其他调优参数(比如连接池大小等) CloseableHttpClient httpClient = httpClientBuilder.build(); |
4)雷区
千万不要使用自定义的ConnectionManager,否则会导致SSL管理工厂失效,无法跳过SSL证书认证。
httpClientBuilder.setConnectionManager(httpClientConnectionManager); // 千万别使用这种代码!! |
原因:
HttpClientBuilder中有这么一段代码,只有当自定义的ConnectionManager为空时,才会使用SSL管理工厂,否则,SSL管理工厂压根不会生效。
HttpClientConnectionManager connManagerCopy = this.connManager; // 自定义的ConnectionManager if (connManagerCopy == null) { LayeredConnectionSocketFactory sslSocketFactoryCopy = this.sslSocketFactory; // 自定义的SSL管理工厂 ... @SuppressWarnings("resource") final Pool |