2012-06-18 37 views
6

Nghĩ rằng tôi sẽ gặp vấn đề tương tự như những người khác, tôi đã trải qua rất nhiều vấn đề tương tự và các giải pháp tiềm năng, nhưng không có may mắn.Java SSL - InstallCert nhận ra chứng chỉ nhưng vẫn không thể tìm thấy lỗi đường dẫn chứng nhận hợp lệ?

Kho lưu trữ tin cậy tôi đang sử dụng là cacerts, nằm trong lib/security của JRE Java 1.6.0 (build 1.6.0_20-b02 ... đây có phải là gốc của vấn đề không?). Tôi cũng đã thử với jssecacerts.

Sử dụng InstallCert (trên các vấn đề tương tự khác được đăng), tôi có thể thấy chứng chỉ của mình đã được cài đặt và hợp lệ (và tôi đã xóa nó, nhập lại, v.v ... để đảm bảo tôi thấy đúng dữ liệu) :

java InstallCert <my host name> 
Loading KeyStore jssecacerts... 
Opening connection to <my host name>:443... 
Starting SSL handshake... 
No errors, certificate is already trusted 

Kiểm tra trong keytool và Portecle, tái nhập khẩu các cERT (tôi đã cố gắng tạo ra từ openssl với -showcert, xuất khẩu từ các trình duyệt và scp'ing nó kết thúc, vv) mang lại cho tôi "đó đã tồn tại dưới bí danh khác ở đây "loại tin nhắn. Vì vậy, có vẻ như không có bất kỳ vấn đề nào với cách chứng chỉ được đưa vào (các) công cụ.

Bắt buộc đường dẫn lưu trữ trong mã không tạo ra bất kỳ sự khác biệt nào và trong mọi trường hợp, tôi sẽ thấy khi tôi bật gỡ lỗi (thông qua setProperty của javax.net.debug thành "tất cả") là:

main, SEND TLSv1 ALERT: fatal, description = certificate_unknown 
main, WRITE: TLSv1 Alert, length = 2 [Raw write]: length = 7 0000: 15 
03 01 00 02 02 2E        ....... main, called 
closeSocket() main, handling exception: 
javax.net.ssl.SSLHandshakeException: 
sun.security.validator.ValidatorException: PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: unable to 
find valid certification path to requested target 

Rất tiếc, tôi không thể cho phép ghi đè séc bằng cách triển khai TrustManager của riêng mình - nó phải thực sự kiểm tra.

Chứng chỉ tôi nhận được từ máy chủ lưu trữ có một số tiện ích (9, chính xác), điều này khiến tôi tự hỏi liệu chúng có phải là một phần của vấn đề này hay không.

Tôi có thể kiểm tra/thử những gì khác? Thay đổi sang phiên bản JRE khác?

+0

Tại sao bạn không thể triển khai 'TrustManager' của riêng mình? Bạn vẫn có thể sử dụng chứng chỉ trong trình quản lý tin cậy và thực hiện kiểm tra. –

+0

@Vivin - Vấn đề cốt lõi mà tôi đang cố giải quyết nằm trong lớp ứng dụng mà tôi không kiểm soát trực tiếp (trong trường hợp này ứng dụng Grails gọi một số dịch vụ web gần đây đã trở thành HTTPS), vì vậy trong khi tôi có thể sử dụng nó trong kịch bản cục bộ của riêng tôi, tôi không thể bắt buộc sử dụng thượng nguồn của nó. Đó là một nỗi đau, nhưng tôi sẽ đối phó với nó sau này - bây giờ nếu tôi có thể làm cho nó hoạt động ở mức độ chi tiết nhất, ít nhất nó cũng cho tôi biết phải đi đâu tiếp theo trong chuỗi giải pháp. – Bill

+0

Bạn có đang chạy ứng dụng này dưới máy chủ ứng dụng như GlassFish không? Bạn có thể kiểm tra xem liệu quá trình đang chạy nhị phân Java trong /usr/lib/jvm/java-1.6.0-sun-1.6.0.20.x86_64 và không phải một số nhị phân Java khác không thích trustStore của bạn (OpenJDK, v.v.) Đầu ra gỡ lỗi bạn đăng là hữu ích, bạn có thể đăng thêm không? – Brad

Trả lời

0

Bạn vẫn có thể kiểm tra chứng chỉ bằng cách triển khai trình quản lý tin cậy của riêng bạn. Tôi đã gặp phải sự cố tương tự here. Tôi cũng đã thử thêm chứng chỉ vào cacerts nhưng không có kết quả.

Trong trình quản lý tin cậy của bạn, bạn cần tải lên rõ ràng chứng chỉ.Về cơ bản những gì tôi phải làm là một cái gì đó như thế này:

Trước tiên tôi tạo ra một nhà quản lý tin tưởng rằng sử dụng các tập tin chứng thực tế:

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

Sau đó, tôi chỉ đă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); 
} 

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

+0

Khó mà tin được tất cả những gì thực sự cần thiết. Có một hương vị khác biệt của việc không hiểu vấn đề cơ bản về tất cả điều này. – EJP

+0

@EJP Không cần phải bảo trợ như vậy. Tôi biết rằng giải pháp này không phải là tối ưu. Nếu bạn có một giải pháp tốt hơn, tôi rất vui khi nghe nó. Tôi đã thử nhiều thứ để làm việc này, bao gồm thêm chứng chỉ (thậm chí tôi đã đảm bảo rằng tôi có thể theo dõi chuỗi tin cậy tất cả các cách) vào tệp cacerts của máy chủ. Không có gì có vẻ hiệu quả. Tôi đã nghiên cứu vấn đề này rất nhiều trước khi sử dụng giải pháp này. Đó không phải là lựa chọn đầu tiên của tôi. –

+0

@Vivin - Đánh giá cao đề xuất, mặc dù tôi thực sự hy vọng tôi không phải đi tuyến đường này. :) Rất nhiều mã khác mà tôi không trực tiếp kiểm soát sẽ phải được thay đổi, do đó, nó là một trong những kịch bản mà tôi tiếp tục hy vọng có một sai lầm ở đâu đó bên cạnh tôi hoặc phía cấu hình máy. – Bill

0

Tôi đoán là một trong những điều sau đã xảy ra:

a) Bạn chạy mã trên máy chủ web. Họ thường sử dụng kho lưu trữ tin cậy của riêng họ - vì vậy bạn có thực sự chắc chắn rằng đó là cacerts đang được sử dụng khi mã của bạn được thực thi không?

b) Theo mặc định, Java sẽ cố kiểm tra tính hợp lệ của chứng chỉ bằng cách tải xuống và diễn giải CRL. Nếu bạn ở sau proxy, tải xuống không thành công và hậu quả là toàn bộ kiểm tra PKIX sẽ không thành công.

+1

Vì anh ấy đang cài đặt chứng chỉ máy chủ trực tiếp, (b) không áp dụng. Câu trả lời tốt khác. – EJP

+0

@EJP Rất tiếc, bạn nói đúng, tôi đã trộn nó bằng cách nhập chứng chỉ gốc thay thế. Sẽ sửa lỗi đó. – emboss

+0

Lúc đầu tôi nghĩ A là nó, sau đó khi tôi đun sôi nó xuống chỉ là mẫu mã nhỏ với đầy đủ các lỗi và nó vẫn thất bại tôi đã bị bummed. Đánh giá cao các đề xuất mặc dù. – Bill

0

Theo dõi gỡ lỗi SSL sẽ hiển thị tệp cacerts bạn đang sử dụng, miễn là bạn không tự tải nó theo cách thủ công. Rõ ràng là bạn không sử dụng cái bạn nghĩ.

+0

Tốt nên kiểm tra, nhưng không phải là trường hợp: "trustStore là: /usr/lib/jvm/java-1.6.0-sun-1.6.0.20.x86_64/jre/lib/security/jssecacerts". Như tôi đã đề cập, tôi đã thử nó với cả jssecacerts và cacerts, bao gồm việc thực thi đường dẫn thông qua các thiết lập trustStore. – Bill

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