进行SSL连接时,PKIX路径构建失败

我正在与名为CommWeb的商家帐户集成,我正在向其url发送SSLpost( https://migs.mastercard.com.au/vpcdps )。 当我尝试发送post时,我得到以下exception:

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 

执行post的代码(我没有写,我们的代码库中已经存在)是:

 public static HttpResponse sendHttpPostSSL(String url, Map params) throws IOException { PostMethod postMethod = new PostMethod(url); for (Map.Entry 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代码:

  

我们的webapp有一个密钥库,我尝试使用keytool命令添加证书(我从firefox导出),但这不起作用,我得到了同样的错误。 我在网上尝试过解决方案(导入密钥并使用System.setProperty ),但这看起来很笨重而且不起作用(给了我一个NoSuchAlgorithmError )。 任何帮助表示赞赏!

显然,valicert class 3 CA证书不在您的默认信任库中(可能是JRE lib / security目录中的cacerts文件,但请参阅JSSE文档以获取完整的故事)。

您可以将此证书添加到cacerts文件中,但我不建议这样做。 相反,我认为您应该创建自己的信任库文件(可以是cacerts文件的副本)并将valicert root ca添加到此文件中。 然后使用javax.net.ssl.trustStore系统属性指向此文件。

我想我应该用我实际做的更新这个答案。 使用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 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 )。