2010-02-18 108 views
16

我正在与名为CommWeb的商户帐户进行集成,并且我正在向其URL(https://migs.mastercard.com.au/vpcdps)发送SSL帖子。当我尝试发送后,我得到以下异常:PKIX路径构建失败,同时进行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)。任何帮助表示赞赏!

+0

http://stackoverflow.com/questions/21076179/pkix-path-building-failed-and-unable-to-find-valid-certification-path-to-requ/36427118#36427118 – MagGGG 2016-07-27 06:18:26

回答

13

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

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

+0

我会试试这个明天。现在,我通过创建一个新的套接字工厂来实现它,该套接字工厂从'commons.httpclient'实现'SecureProtocolSocketFactory'。它盲目接受证书。但是,我想改变这一点并使其以正确的方式工作。我会让你知道发生了什么。谢谢! – 2010-02-19 05:24:40

+0

我只想继续并接受您的解决方案,并将其添加为评论。只有在查看了您指出的文档后,我才能够理解它! – 2010-02-22 00:53:57

+0

格雷格,你能向我解释“加上valicert root ca”吗?这是什么意思,应该怎么做? – Less 2012-09-25 13:22:33

7

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

相关问题