RestTemplate连接https,证书匹配失败

首页 / 新闻资讯 / 正文

First Vector Graphic

RestTemplate连接https,证书匹配失败

使用RestTemplate进行接口转发,转发地址是 https://内网IP 的格式,由于证书是自签的,所以在调试的时候

接口报错,在jvm中无法找到匹配的SSL证书:

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target] with root cause 

所以在浏览器访问 https://内网IP ,并通过浏览器将证书下载下来,安装到我们的JDK:

# 进入到安装的jre目录,并将下载下来的证书拷贝到当前目录 cd $JAVA_HOME/jre/lib/security  # 执行安装命令 keytool -import -alias 证书别名 -keystore cacerts -file ./xxxx.cer 

经过上面的操作后重启服务,调用接口发现还是报同样的错误,但是SSL证书确实是安装成功的,所以索性在服务生成RestTemplate的Bean时绕过SSL验证,一般情况下,会通过SimpleClientHttpRequestFactory来构建RestTemplate实例,但是它不支持https,要支持https需要替换SimpleClientHttpRequestFactory为HttpComponentsClientHttpRequestFactory,而且我们还要引入httpclient依赖:

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

修改RestTemplateConfig配置类:

@Configuration public class RestTemplateConfig {     @Bean     public RestTemplate restTemplate(ClientHttpRequestFactory factory){         return new RestTemplate(factory);     }      @Bean     public ClientHttpRequestFactory httpComponentsClientHttpRequestFactory() throws Exception {         // 自定义证书校验器         TrustManager[] trustAllCerts = new TrustManager[]{                 new X509TrustManager() {                     @Override                     public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) {                     }                      @Override                     public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) {                     }                      @Override                     public java.security.cert.X509Certificate[] getAcceptedIssuers() {                         return new X509Certificate[0];                     }                 }         };          SSLContext sslContext = SSLContext.getInstance("SSL");         sslContext.init(null, trustAllCerts, new java.security.SecureRandom());           SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);          CloseableHttpClient httpClient = HttpClients.custom()                 .setSSLSocketFactory(csf)                 .build();         HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();         factory.setConnectTimeout(20000);         factory.setReadTimeout(15000);         factory.setHttpClient(httpClient);         return factory;     } } 

重启服务调用接口,原以为应该没什么大问题了,可是报了不同的错误:

Certificate for <10.xx.xx.xx> doesn't match any of the subject alternative names: [localhost, xx.xx.com] 

从上面的错误提示可以看出,其实第一步安装的证书是成功的,但是在创建SSL连接验证域名的时候出错了,打开证书查看证书保护的域名只有 localhost, xx.xx.com ,所以这是报错的根源所在,我通过IP访问的链接,但是IP不在证书保护的域名内,导致验证错误,但是我通过域名访问的链接 https://域名 是成功的。

网上查找了原因,我引入的httpclient版本是4.4以上的,所以在生成SSLConnectionSocketFactory实例时,需要指定host验证器,允许ssl连接允许通过不同的hosts,

SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); 

完整的RestTemplateConfig配置类:

@Configuration public class RestTemplateConfig {     @Bean     public RestTemplate restTemplate(ClientHttpRequestFactory factory){         return new RestTemplate(factory);     }      @Bean     public ClientHttpRequestFactory httpComponentsClientHttpRequestFactory() throws Exception {         // 自定义证书校验器         TrustManager[] trustAllCerts = new TrustManager[]{                 new X509TrustManager() {                     @Override                     public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) {                     }                      @Override                     public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) {                     }                      @Override                     public java.security.cert.X509Certificate[] getAcceptedIssuers() {                         return new X509Certificate[0];                     }                 }         };          SSLContext sslContext = SSLContext.getInstance("SSL");         sslContext.init(null, trustAllCerts, new java.security.SecureRandom());           SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);          CloseableHttpClient httpClient = HttpClients.custom()                 .setSSLSocketFactory(csf)                 .build();         HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();         factory.setConnectTimeout(20000);         factory.setReadTimeout(15000);         factory.setHttpClient(httpClient);         return factory;     } }