PKIX 路径构建失败:无法找到请求目标的有效证书路径
Posted
技术标签:
【中文标题】PKIX 路径构建失败:无法找到请求目标的有效证书路径【英文标题】:PKIX path building failed: unable to find valid certification path to requested target 【发布时间】:2011-05-03 00:22:54 【问题描述】:我正在调用以下客户端的一些 HTTPS Web 服务:
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
/**
* Handles http and https connections. It sends XML request over http (or https)
* to SOAP web service and receive the XML reply.
*
* @author mhewedy
* @date 30.10.2010
*/
public class HttpWSXmlClient
private final String ws_url;
private byte[] requestData;
public HttpWSXmlClient(String wsUrl)
this.ws_url = wsUrl;
public void readRequest(String xmlRequestFilePath)
try
InputStream istream = new FileInputStream(xmlRequestFilePath);
byte[] data = stream2Bytes(istream);
istream.close();
this.requestData = data;
catch (Exception e)
throw new RuntimeException(e.getMessage());
/**
*
* @param ps
* PrintStream object to send the debugging info to.
* @return
* @throws IOException
*/
public byte[] sendAndRecieve(PrintStream ps) throws IOException
if (requestData == null)
throw new RuntimeException(
"the request data didn't initialized yet.");
if (ps != null)
ps.println("Request:\n" + new String(requestData));
URL url = new URL(ws_url);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
// or HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("content-type", "text/xml");
connection.connect();
OutputStream os = connection.getOutputStream();
os.write(requestData);
InputStream is = connection.getInputStream();
byte[] rply = stream2Bytes(is);
if (ps != null)
ps.println("Response:\n" + new String(rply));
os.close();
is.close();
connection.disconnect();
return rply;
public byte[] sendAndRecieve() throws IOException
return sendAndRecieve(null);
private byte[] stream2Bytes(InputStream istream) throws IOException
ByteArrayOutputStream outstream = new ByteArrayOutputStream();
int c;
while ((c = istream.read()) != -1)
if (c != 0x0A && c != 0x0D) // prevent new line character from being
// written
if (c == 0x09)
c = 0x20; // prevent tab character from being written,
// instead write single space char
outstream.write(c);
byte[] ret = outstream.toByteArray();
outstream.close();
return ret;
测试:
public class Test
private static final String WS_URL = "https://some_server/path/to/ws";
public static void main(String[] args) throws Exception
HttpWSXmlClient client = new HttpWSXmlClient(WS_URL);
client.readRequest("request.xml");
client.sendAndRecieve(System.out);
我得到以下输出:
Exception in thread "Main Thread" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1591)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:187)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:181)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1035)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:124)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:516)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:454)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:884)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1096)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1123)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1107)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:415)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)
at com.se.swstest.HttpWSXmlClient.sendAndRecieve(HttpWSXmlClient.java:63)
at com.se.swstest.Test.main(Test.java:11)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:285)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:191)
at sun.security.validator.Validator.validate(Validator.java:218)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1014)
... 12 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:280)
... 18 more
我是否需要将任何证书放在 jdk/jre/lib/security 中? 另外,我有一个 xxx_IE.crt 和 xxx_FX.crt(分别用于 Firefox 和 IE,它们不适用于上述 Java 客户端,所以我需要 Java 客户端的特定证书吗?
谢谢。
【问题讨论】:
这可能会有所帮助:***.com/questions/9210514/… 【参考方案1】:您需要设置证书才能访问此网址。使用以下代码设置密钥库:
System.setProperty("javax.net.ssl.trustStore","clientTrustStore.key");
System.setProperty("javax.net.ssl.trustStorePassword","qwerty");
【讨论】:
这实际上对我有用,因为我将信任库设置为我的whatever.jks
文件,尽管我不确定为什么。不过谢谢。
在这里运行良好..虽然我什至不知道 trustStore 是什么:/
信任库不包含密钥,因此将其命名为 .key 是没有意义的。
谁能说出 qwerty 和 clientTrustStore.key 的真正价值是什么?
我必须指出,为整个 JVM 设置信任库是一个非常糟糕的主意。它可能会劫持另一个组件所期望的设置,并且根据您放入信任存储的内容,可能会降低在该 JVM 中运行的任何代码的安全性。最好只为您打算建立的连接设置信任库(和相关配置)。您正在使用HttpsURLConnection
,您可以在这些连接上设置信任管理器,而不会有太多麻烦。【参考方案2】:
Java 8 解决方案:我刚刚遇到了这个问题,并通过将远程站点的证书添加到我的 Java 密钥库来解决它。我的解决方案基于solution at the myshittycode blog,它基于previous solution in mykong's blog。这些博客文章解决方案归结为下载一个名为InstallCert 的程序,这是一个Java 类,您可以从命令行运行以获取证书。然后继续在 Java 的密钥库中安装证书。
InstallCert Readme 非常适合我。您只需要运行以下命令:
javac InstallCert.java
java InstallCert [host]:[port]
(在运行命令时输入要在列表中添加的证书的给定列表 number - 可能只是 1)
keytool -exportcert -alias [host]-1 -keystore jssecacerts -storepass changeit -file [host].cer
sudo keytool -importcert -alias [host] -keystore [path to system keystore] -storepass changeit -file [host].cer
如果需要,请参阅引用的 README 文件以获取示例。
【讨论】:
请注意,系统的密钥库可能位于:$JAVA_HOME/jre/lib/security/cacerts
。
有没有办法将 Java 应用程序指向 .cer 文件而不是修改系统的密钥库?
@golimar 我不这么认为(see this Oracle page 和 this question)。话虽如此,您的问题对于 *** 社区来说可能是一个有价值的新问题。
我想我已经结合了您的答案(生成 jssecacerts 文件)文件和@Vipin Kumar 的答案(将应用程序指向该文件)
如前所述,我使用InstallCerts.java
,但不是在部署应用程序的地方更新JDK 密钥库,而是将InstallCert.java
生成的密钥库(名为jssecacerts 的文件)与应用程序捆绑在一起。只需将其放在类路径中即可。这样就不需要更新JDK了。【参考方案3】:
我遇到过几次,这是由于证书链不完整。如果您使用的是标准 java 信任库,它可能没有完成证书链所需的证书,而该证书链是验证您连接到的 SSL 站点的证书所必需的。
我在使用一些 DigiCert 证书时遇到了这个问题,不得不自己手动添加中间证书。
【讨论】:
我也有一个 DigiCert,我想也有你描述的同样的问题。就我而言,我无法从浏览器中导出整个证书链以将它们全部添加到信任库中。你能分享一下你的做法吗? 你用的是什么浏览器?我相信我是用 Firefox 来做的,虽然我不记得确切的方法。 我以前用过IE。现在我可以使用 FireFox 将其导出,成功添加到信任库,但仍然有相同的异常。我会继续努力。还是谢谢! 实际上 Webserver 应该发送链中的所有证书。是Java客户端实现的问题无法处理吗?你知道这方面的细节吗?【参考方案4】:这是我用于将站点的公共证书安装到系统密钥库中以供使用的解决方案。
使用以下命令下载证书:
unix、linux、mac
openssl s_client -connect [host]:[port|443] < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > [host].crt
窗口
openssl s_client -connect [host]:[port|443] < NUL | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > [host].crt
这将创建一个可用于导入密钥库的 crt。
使用以下命令安装新证书:
keytool -import -alias "[host]" -keystore [path to keystore] -file [host].crt
这将允许您从导致异常的站点导入新证书。
【讨论】:
许多现代服务器使用 SNI;如果是这样,s_client
要获得正确的证书,请添加 -servername $host
。在 java7 up 中,-importcert
可以处理 PEM 文件中的无关文本,因此您不需要 sed
;同样在 j7 up 中,keytool -printcert -sslserver $host[:$port] -rfc
获取证书链并在 PEM 中打印(例如 s_client -showcerts
没有多余的文本)如果您没有安装 OpenSSL,这会更容易,例如窗户。【参考方案5】:
我在尝试从 Java 代码发起 SOAP 请求时遇到了这个问题。对我有用的是:
通过在浏览器中点击 URL 获取服务器证书:http://docs.bvstools.com/home/ssl-documentation/exporting-certificate-authorities-cas-from-a-website 此链接包含获取服务器证书的所有步骤
获得服务器证书后,请关注http://java.globinch.com/enterprise-java/security/pkix-path-building-failed-validation-sun-security-validatorexception/#Valid-Certification-Path-to-Requested-Target。
复制链接中的文本,以防链接失效:
修复此错误所需要做的就是添加服务器证书 到您信任的 Java 密钥库。首先你需要下载 来自服务器的文档。
在您的硬盘驱动器中拥有证书后,您可以将其导入到 Java 信任库。将证书导入受信任的 Java 密钥库,您可以使用 java ‘keytool’ 工具。在命令提示符下 导航到 JRE bin 文件夹,在我的情况下,路径是:C:\Program 文件\Java\jdk1.7.0_75\jre\bin 。然后使用keytool命令如下 将证书导入 JRE。
keytool -import -alias _alias_name_ -keystore ..\lib\security\cacerts -file _path_to_cer_file
它会要求输入密码。默认情况下,密码是“changeit”。如果 密码不同可能无法导入 证书。
【讨论】:
【参考方案6】:如果您不需要 SSL 安全性,那么您可能需要将其关闭。
/**
* disable SSL
*/
private void disableSslVerification()
try
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]
new X509TrustManager()
public java.security.cert.X509Certificate[] getAcceptedIssuers()
return null;
public void checkClientTrusted(X509Certificate[] certs,
String authType)
public void checkServerTrusted(X509Certificate[] certs,
String authType)
;
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier()
public boolean verify(String hostname, SSLSession session)
return true;
;
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
catch (NoSuchAlgorithmException e)
e.printStackTrace();
catch (KeyManagementException e)
e.printStackTrace();
【讨论】:
【参考方案7】:如果服务器只发送其叶子证书,而没有将构建信任链所需的所有链证书发送到根 CA,也会发生此错误。不幸的是,这是服务器的常见错误配置。
如果大多数浏览器已经从之前的访问中知道丢失的链证书,或者如果叶证书包含 Authority Information Access (AIA) 中 CA 颁发者的 URL,则可能会下载丢失的证书,从而解决此问题.但这种行为通常仅限于桌面浏览器和其他工具,因为它们无法建立信任链而完全失败。
您可以通过将com.sun.security.enableAIAcaIssuers
设置为true
,使JRE自动下载中间证书
要验证服务器是否正在发送所有链证书,您可以在以下 SSL 证书验证工具中输入主机 https://www.digicert.com/help/
【讨论】:
【参考方案8】:在 Mac OS 上,我必须使用系统钥匙串访问工具打开服务器的自签名证书,导入它,双击它,然后选择“始终信任”(即使我在导入器中设置相同)。 在此之前,当然我运行 java key take with -importcert 将相同的文件导入 cacert 存储。
【讨论】:
【参考方案9】:对我来说,我在调用webservice调用时遇到了这个错误,请确保站点具有有效的ssl,即检查url侧面的徽标,否则需要将证书添加到受信任的密钥库中你的机器
【讨论】:
【参考方案10】:我也遇到过这类问题。我使用的是 tomcat 服务器,然后我将认可的文件夹放入 tomcat 中,然后它开始工作。而且我用 1.7 替换了 JDK1.6,然后它也工作了。最后我学习了 SSL,然后我解决了这个问题问题类型。首先,您需要从该服务提供商服务器下载证书。然后您的握手是成功的。 1.尝试将认可的文件夹放在您的服务器中 下一条路 2.使用jdk1.7
下一步 3.尝试使用SSL下载有效证书
【讨论】:
这不能回答问题。背书文件夹是什么意思?以上是关于PKIX 路径构建失败:无法找到请求目标的有效证书路径的主要内容,如果未能解决你的问题,请参考以下文章
“PKIX 路径构建失败”和“无法找到请求目标的有效证书路径”
“PKIX 路径构建失败:无法找到请求目标的有效证书路径”仅在发布版本中
javax.mail.MessagingException:PKIX 路径构建失败:SunCertPathBuilderException:无法找到请求目标的有效证书路径;
无法找到请求的目标 PKIX 路径构建的有效证书路径失败:sun.security.provider.certpath.SunCertPathBuilderException