Java代码调用https(SSL证书验证问题)
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 cause
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at 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 {
@Override
protected 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 {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
}
private static class SkipX509TrustManager implements X509TrustManager {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
}
}
// 重新封装RestTemplate实例
/**
* @description:
* @author: laizhenghua
* @date: 2022/7/29 17:04
*/
@Configuration
public class RestTemplateConfiguration {
@Bean
public 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接口,就不报错了。