建立 SSL 连接时,PKIX 路径构建失败
Posted
技术标签:
【中文标题】建立 SSL 连接时,PKIX 路径构建失败【英文标题】:PKIX path building failed while making SSL connection 【发布时间】:2011-01-18 10:25:15 【问题描述】:我正在与一个名为 CommWeb 的商家帐户集成,并且我正在向他们的 URL (https://migs.mastercard.com.au/vpcdps) 发送 SSL 帖子。当我尝试发送帖子时,出现以下异常:
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
执行该帖子的代码(我没有编写,并且已经存在于我们的代码库中)是:
public static HttpResponse sendHttpPostSSL(String url, Map<String, String> params) throws IOException
PostMethod postMethod = new PostMethod(url);
for (Map.Entry<String, String> entry : params.entrySet())
postMethod.addParameter(entry.getKey(), StringUtils.Nz(entry.getValue()));
HttpClient client = new HttpClient();
int status = client.executeMethod(postMethod);
if (status == 200)
StringBuilder resultBuffer = new StringBuilder();
resultBuffer.append(postMethod.getResponseBodyAsString());
return new HttpResponse(resultBuffer.toString(), "");
else
throw new IOException("Invalid response code: " + status);
商家帐户集成的文档没有提及证书。他们确实提供了一些似乎盲目接受证书的示例 JSP 代码:
<%! // Define Static Constants
// ***********************
public static X509TrustManager s_x509TrustManager = null;
public static SSLSocketFactory s_sslSocketFactory = null;
static
s_x509TrustManager = new X509TrustManager()
public X509Certificate[] getAcceptedIssuers() return new X509Certificate[] ;
public boolean isClientTrusted(X509Certificate[] chain) return true;
public boolean isServerTrusted(X509Certificate[] chain) return true;
;
java.security.Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
try
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[] s_x509TrustManager , null);
s_sslSocketFactory = context.getSocketFactory();
catch (Exception e)
e.printStackTrace();
throw new RuntimeException(e.getMessage());
...
...
// write output to VPC
SSLSocket ssl = (SSLSocket)s_sslSocketFactory.createSocket(s, vpc_Host, vpc_Port, true);
ssl.startHandshake();
os = ssl.getOutputStream();
// get response data from VPC
is = ssl.getInputStream();
...
...
%>
我们的 web 应用程序有一个密钥库,我尝试使用 keytool
命令添加证书(我从 firefox 导出的),但这没有用,我得到了同样的错误。我已经在网上尝试过解决方案(导入密钥并使用System.setProperty
),但这似乎有点笨拙并且不起作用(给了我NoSuchAlgorithmError
)。任何帮助表示赞赏!
【问题讨论】:
***.com/questions/21076179/… 【参考方案1】:很明显,valicert class 3 CA 证书不在您的默认信任库中(这可能是您的 JRE lib/security 目录中的 cacerts 文件,但请参阅JSSE documentation 了解完整内容)。
您可以将此证书添加到 cacerts 文件中,但我不建议这样做。相反,我认为您应该创建自己的信任库文件(可以是 cacerts 文件的副本)并将 valicert root ca 添加到此文件中。然后用javax.net.ssl.trustStore
系统属性指向这个文件。
【讨论】:
我明天试试这个。现在,我通过创建一个从commons.httpclient
实现SecureProtocolSocketFactory
的新套接字工厂来实现它。它盲目地接受证书。但是,我想改变它并让它以正确的方式工作。我会让你知道会发生什么。谢谢!
我将继续接受您的解决方案并将我的解决方案添加为评论。只有在查看了您指向我的文档后,我才能弄清楚!
Greg,你能向我解释一下“将 valicert root ca 添加到这个”。这是什么意思,应该怎么做?【参考方案2】:
我想我应该用我实际所做的来更新这个答案。使用 GregS 提供的文档,我为 valicert 创建了一个信任管理器。在信任管理器中,我加载了证书文件:
public class ValicertX509TrustManager implements X509TrustManager
X509TrustManager pkixTrustManager;
ValicertX509TrustManager() throws Exception
String valicertFile = "/certificates/ValicertRSAPublicRootCAv1.cer";
String commwebDRFile = "/certificates/DR_10570.migs.mastercard.com.au.crt";
String commwebPRODFile = "/certificates/PROD_10549.migs.mastercard.com.au.new.crt";
Certificate valicert = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(valicertFile));
Certificate commwebDR = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebDRFile));
Certificate commwebPROD = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebPRODFile));
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, "".toCharArray());
keyStore.setCertificateEntry("valicert", valicert);
keyStore.setCertificateEntry("commwebDR", commwebDR);
keyStore.setCertificateEntry("commwebPROD", commwebPROD);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
trustManagerFactory.init(keyStore);
TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();
for(TrustManager trustManager : trustManagers)
if(trustManager instanceof X509TrustManager)
pkixTrustManager = (X509TrustManager) trustManager;
return;
throw new Exception("Couldn't initialize");
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException
pkixTrustManager.checkServerTrusted(chain, authType);
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
pkixTrustManager.checkServerTrusted(chain, authType);
public X509Certificate[] getAcceptedIssuers()
return pkixTrustManager.getAcceptedIssuers();
现在,使用这个信任管理器,我必须创建一个套接字工厂:
public class ValicertSSLProtocolSocketFactory implements ProtocolSocketFactory
private SSLContext sslContext = null;
public ValicertSSLProtocolSocketFactory()
super();
private static SSLContext createValicertSSLContext()
try
ValicertX509TrustManager valicertX509TrustManager = new ValicertX509TrustManager();
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new ValicertX509TrustManager[] valicertX509TrustManager, null);
return context;
catch(Exception e)
Log.error(Log.Context.Net, e);
return null;
private SSLContext getSSLContext()
if(this.sslContext == null)
this.sslContext = createValicertSSLContext();
return this.sslContext;
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException
return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException
if(params == null)
throw new IllegalArgumentException("Parameters may not be null");
int timeout = params.getConnectionTimeout();
SocketFactory socketFactory = getSSLContext().getSocketFactory();
if(timeout == 0)
return socketFactory.createSocket(host, port, localAddress, localPort);
else
Socket socket = socketFactory.createSocket();
SocketAddress localAddr = new InetSocketAddress(localAddress, localPort);
SocketAddress remoteAddr = new InetSocketAddress(host, port);
socket.bind(localAddr);
socket.connect(remoteAddr, timeout);
return socket;
public Socket createSocket(String host, int port) throws IOException
return getSSLContext().getSocketFactory().createSocket(host, port);
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
public boolean equals(Object obj)
return ((obj != null) && obj.getClass().equals(ValicertSSLProtocolSocketFactory.class));
public int hashCode()
return ValicertSSLProtocolSocketFactory.class.hashCode();
现在我只是注册一个新协议:
Protocol.registerProtocol("vhttps", new Protocol("vhttps", new ValicertSSLProtocolSocketFactory(), 443));
PostMethod postMethod = new PostMethod(url);
for (Map.Entry<String, String> entry : params.entrySet())
postMethod.addParameter(entry.getKey(), StringUtils.Nz(entry.getValue()));
HttpClient client = new HttpClient();
int status = client.executeMethod(postMethod);
if (status == 200)
StringBuilder resultBuffer = new StringBuilder();
resultBuffer.append(postMethod.getResponseBodyAsString());
return new HttpResponse(resultBuffer.toString(), "");
else
throw new IOException("Invalid response code: " + status);
唯一的缺点是我必须为这个特定的证书创建一个特定的协议 (vhttps
)。
【讨论】:
以上是关于建立 SSL 连接时,PKIX 路径构建失败的主要内容,如果未能解决你的问题,请参考以下文章
SSLHandshakeException:PKIX 路径构建失败
spring boot ssl 客户端证书失败,PKIX 路径构建失败错误
javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIX 路径构建失败 [重复]
javax.net.ssl.SSLHandshakeException:PKIX 路径构建失败:sun.security.provider.certpath.SunCertPathBuilderExc
Java -> Flask Https 请求:javax.net.ssl.SSLHandshakeException:PKIX 路径构建失败