Java调用HTTPS接口出现sun.security.validator.ValidatorException: PKIX path valida异常解析及解决方案
介绍
在Java开发中,我们经常会使用HTTP协议调用远程接口。而当我们需要调用HTTPS接口时,会遇到sun.security.validator.ValidatorException: PKIX path valida
异常。本文将从原理、出现原因和解决方案三个方面来进行详细讲解。
问题原因
在使用Java调用HTTPS接口时,会涉及到证书的验证。当Java客户端发起HTTPS请求时,会验证远程服务器返回的证书是否可信。如果证书不可信,就会抛出sun.security.validator.ValidatorException: PKIX path valida
异常。
这个异常主要有以下几个常见的原因:
-
服务器证书不受信任:Java默认信任的证书存储在JDK的
cacerts
文件中,如果服务器证书不在信任列表中,就会抛出异常。 -
证书链不完整:服务器返回的证书链可能不完整,缺少中间证书。而Java为了验证证书的合法性,需要完整的证书链。
-
证书过期:如果服务器的证书已经过期,Java也会认为证书不可信。
解决方案
针对上述问题,我们可以采取以下几种解决方案。
方案一:信任所有证书
我们可以通过创建一个信任所有证书的TrustManager
来解决该问题。代码示例如下:
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.*;
public class TrustAllManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
// 在调用HTTPS接口前,添加以下代码
TrustManager[] trustAllCerts = new TrustManager[] { new TrustAllManager() };
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
该方案虽然解决了证书验证的问题,但是也存在一定的安全风险,因为它会信任所有的证书,包括无效或不受信任的证书。
方案二:添加缺失的中间证书
在服务器返回的证书链中,可能缺少一些中间证书。我们可以通过将缺失的中间证书添加到信任列表中来解决该问题。
-
查找缺失的中间证书 可以使用在线工具或通过命令行工具(如
openssl
)来查找缺失的中间证书。一般来说,服务器的SSL证书提供商会有相关的中间证书下载页面。 -
下载中间证书 下载所有缺失的中间证书,保存为
.cer
或.crt
格式的文件。 -
导入中间证书 将下载的中间证书导入到Java的
cacerts
文件中:keytool -import -alias intermediate -file intermediate.crt -keystore cacerts
其中,
intermediate.crt
是下载的中间证书文件。 -
使用SSL证书验证 在调用HTTPS接口前,添加以下代码,告诉Java使用导入的证书进行验证:
System.setProperty("javax.net.ssl.trustStore", "path/to/cacerts");
其中,
path/to/cacerts
是cacerts
文件的路径。
方案三:使用自定义证书
如果你有自己的证书,可以通过以下方式来解决该问题。
-
导入自定义证书 将自定义证书导入到Java的
cacerts
文件中:keytool -import -alias custom -file custom.crt -keystore cacerts
其中,
custom.crt
是自定义证书文件。 -
使用自定义证书验证 在调用HTTPS接口前,添加以下代码,告诉Java使用自定义证书进行验证:
System.setProperty("