2010-02-18 25 views
16

Tôi đang tích hợp với Tài khoản người bán được gọi là CommWeb và tôi đang gửi một bài đăng SSL tới URL của họ (https://migs.mastercard.com.au/vpcdps). Khi tôi cố gắng gửi bài, tôi nhận được ngoại lệ sau đây:Xây dựng đường dẫn PKIX không thành công khi thực hiện kết nối SSL

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

Mã (mà tôi đã không viết, và điều đó đã tồn tại trong codebase của chúng tôi) mà thực hiện các bài viết là:

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); 
    } 
} 

Tài liệu về tích hợp Tài khoản người bán không nói gì về chứng chỉ. Họ đã cung cấp một số mẫu JSP mã mà dường như mù quáng chấp nhận chứng chỉ:

<%! // 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(); 
... 
... 
%> 

webapp của chúng tôi có một keystore, và tôi đã cố gắng bổ sung thêm giấy chứng nhận (mà tôi xuất khẩu từ firefox) bằng cách sử dụng lệnh keytool, nhưng điều đó không làm việc và tôi gặp lỗi tương tự. Tôi đã thử các giải pháp trên web (nhập khóa và sử dụng System.setProperty) nhưng điều đó có vẻ như là loại clunky và nó không hoạt động (đã cho tôi một NoSuchAlgorithmError). Bất kỳ trợ giúp được đánh giá cao!

+0

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

Trả lời

13

Rõ ràng chứng chỉ CA cấp 3 chứng chỉ không có trong kho lưu trữ mặc định của bạn (có thể là tệp cacerts trong thư mục bảo mật/lib JRE của bạn, nhưng xem JSSE documentation cho toàn bộ câu chuyện).

Bạn có thể thêm chứng chỉ này vào tệp cacerts, nhưng tôi không khuyến nghị điều này. Thay vào đó, tôi nghĩ rằng bạn nên tạo tập tin truststore của riêng bạn (có thể là một bản sao của tập tin cacerts) và thêm ca gốc valicert vào đây. Sau đó trỏ đến tệp này với thuộc tính hệ thống javax.net.ssl.trustStore.

+0

Tôi sẽ dùng thử Ngày mai. Bây giờ, tôi đã làm cho nó hoạt động bằng cách tạo ra một nhà máy socket mới thực hiện 'SecureProtocolSocketFactory' từ' commons.httpclient'. Nó chấp nhận một cách mù quáng chứng chỉ. Tuy nhiên, tôi muốn thay đổi điều này và làm cho nó hoạt động đúng cách. Tôi sẽ cho bạn biết những gì sẽ xảy ra. Cảm ơn! –

+0

Tôi sẽ tiếp tục và chấp nhận giải pháp của bạn và thêm tôi làm nhận xét. Tôi đã có thể tìm ra nó chỉ sau khi xem tài liệu mà bạn đã chỉ cho tôi! –

+0

Greg, bạn có thể giải thích cho tôi "thêm ca gốc valicert vào cái này" không. Nó có nghĩa là gì, và nó nên được thực hiện như thế nào? – Less

7

Tôi hình tôi nên cập nhật câu trả lời này với những gì tôi thực sự đã làm. Sử dụng tài liệu mà GregS cung cấp, tôi đã tạo một trình quản lý tin cậy cho valicert. Trong quản lý tin tưởng, tôi tải các tập tin giấy chứng nhận:

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(); 
    } 
} 

Bây giờ, sử dụng quản lý tin tưởng này, tôi đã phải tạo ra một nhà máy ổ cắm:

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(); 
    } 
} 

Bây giờ tôi chỉ cần đăng ký một giao thức mới:

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); 
} 

Điểm bất lợi duy nhất là tôi phải tạo giao thức cụ thể (vhttps) cho chứng chỉ cụ thể này.

Các vấn đề liên quan