Java代码调用https接口SSL证书验证问题
现有一个https接口,如下
@Test
public void test1() {String url = "https://iservericloudhx.yndk.cn:32613/iserver/services/map-mongodb-C_YGYX_530000_2022/wms111/C_YGYX_530000_2022";RestTemplate restTemplate = new RestTemplate();String response = restTemplate.getForObject(url, String.class);log.info("-----map get ----- reponse {}", response);
}
直接执行代码,会报如下错误
threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://iservericloudhx.yndk.cn:32613/iserver/services/map-mongodb-C_YGYX_530000_2022/wms111/C_YGYX_530000_2022": PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target] with root causesun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested targetat sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141) ~[na:1.8.0_261]at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126) ~[na:1.8.0_261]at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280) ~[na:1.8.0_261]at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:434) ~[na:1.8.0_261]at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:306) ~[na:1.8.0_261]at sun.security.validator.Validator.validate(Validator.java:271) ~[na:1.8.0_261]
这是因为Java在访问SSL加密的网站时,需要从JDK的KeyStore(存储位置为%JAVA_HOME%\jre\lib\security\cacerts
)里面查找相对应的证书,如果不能找到就会报以上错误!
如何解决这个问题呢?
方式1:绕过SSL证书验证。
// 如果使用的是 apache 的http工具
String url = "https://xxx";
HttpClient httpClient = SkipHttpsUtil.wrapClient();
HttpGet httpGet = new HttpGet(url);
try {HttpResponse response = httpClient.execute(httpGet);HttpEntity entity = response.getEntity();if (ObjectUtil.isNull(entity)) {return null;}String res = EntityUtils.toString(entity, "utf-8");return JSON.parseObject(res);
} catch (Exception ex) {ex.printStackTrace();log.error("execute error ---> ", ex);
}// 如果使用的 RestTemplate
// 重写SimpleClientHttpRequestFactory类
/*** @description:* @author: laizhenghua* @date: 2022/8/24 11:16*/
public class RequestFactory extends SimpleClientHttpRequestFactory {@Overrideprotected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {if (connection instanceof HttpsURLConnection) {prepareHttpsConnection((HttpsURLConnection) connection);}super.prepareConnection(connection, httpMethod);}private void prepareHttpsConnection(HttpsURLConnection connection) {connection.setHostnameVerifier(new SkipHostnameVerifier());try {connection.setSSLSocketFactory(createSslSocketFactory());} catch (Exception ex) {// Ignore}}private SSLSocketFactory createSslSocketFactory() throws Exception {javax.net.ssl.SSLContext context = javax.net.ssl.SSLContext.getInstance("TLS");context.init(null, new TrustManager[] { new SkipX509TrustManager() }, new SecureRandom());return context.getSocketFactory();}private class SkipHostnameVerifier implements HostnameVerifier {@Overridepublic boolean verify(String s, SSLSession sslSession) {return true;}}private static class SkipX509TrustManager implements X509TrustManager {@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) {}}
}// 重新封装RestTemplate实例
/*** @description:* @author: laizhenghua* @date: 2022/7/29 17:04*/
@Configuration
public class RestTemplateConfiguration {@Beanpublic RestTemplate restTemplate() {RequestFactory factory = new RequestFactory(); // 这里RequestFactory就是上述重写的factory.setReadTimeout(30000);factory.setConnectTimeout(30000);return new RestTemplate(factory);}
}
方式2:将所访问的SSL站点证书从浏览器导出,并通过keytool
命令导入到JDK的证书库中。
1、先从浏览器导出SSL证书(详见下图)
会得到一个证书文件!
2、将证书文件导入到JDK的证书库中,详见以下命令
# 注意点1: keytool 命令如果使用不了,就从Java安装bin目录cmd进行操作,如 cmd E:\jdk1.8\bin
# 注意点2:执行 keytool 命令会提示"输入密钥库口令:" 口令是changeit 如下
# C:\Users\赖正华>keytool -list -alias _.yndk.cn -v -keystore "%JAVA_HOME%\jre\lib\security\cacerts"
# 输入密钥库口令: changeit# 导入
keytool -import -alias _.yndk.cn -keystore "%JAVA_HOME%\jre\lib\security\cacerts" -file E:\_.yndk.cn.crt
# -alias 指定证书别名一般设置域名即可
# -keystore 指定证书导入位置
# -file 指定证书文件(就是浏览器导出的证书文件)# 删除
keytool -delete -alias _.yndk.cn -keystore "%JAVA_HOME%\jre\lib\security\cacerts"# 查看
keytool -list -alias _.yndk.cn -v -keystore "%JAVA_HOME%\jre\lib\security\cacerts"
导入证书后我们再来访问https接口,就不报错了。