RestTemplate连接https,证书匹配失败

2023年2月25日12:59:21

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

  • 作者:Kesling
  • 原文链接:https://blog.csdn.net/weixin_43819744/article/details/122809493
    更新时间:2023年2月25日12:59:21 ,共 3555 字。