我有一个商家帐户名为CommWeb整合和我发送一个SSL讯息到他们的URL(https://migs.mastercard.com.au/vpcdps).当我尝试发送帖子时,我得到以下异常:
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, Mapparams) 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代码:
<%! // 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应用程序有一个密钥库,我试图将证书使用(这是我从Firefox导出)keytool
命令,但没有工作,我得到了同样的错误.我已经在网上尝试过解决方案(导入密钥和使用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.Entryentry : 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
为这个特定的证书创建一个特定的协议().