2012-06-18 14 views
6

Myśląc, że trafiłem na ten sam problem, co inni ludzie, przechodziłem przez wiele podobnych problemów i potencjalnych rozwiązań, ale bez powodzenia.Java SSL - InstallCert rozpoznaje certyfikat, ale nadal "nie można znaleźć prawidłowej ścieżki certyfikacji" błąd?

Magazyn zaufanych certyfikatów, którego używam, to cacerts, znajdujący się w bibliotece lib/security środowiska Java 1.6.0 JRE (kompilacja 1.6.0_20-b02 ... czy może to być przyczyną problemu?). Próbowałem również z jssecacerts.

Korzystanie z InstallCert (na inne podobne problemy opublikowane), widzę, że mój certyfikat jest rzeczywiście zainstalowany i ważny (i usunąłem go, ponownie zaimportowałem, itp., Aby upewnić się, że widzę odpowiednie dane) :

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

Sprawdzanie w keytool i Portecle, ponowne importowanie cert (próbowałem generowania z OpenSSL z -showcert, eksport z przeglądarek i scp'ing go, itp) daje mi „, która istnieje już pod ten inny alias tutaj "typ wiadomości. Tak więc nie wydaje się, aby istniał jakiś problem związany ze sposobem, w jaki cert dostaje się do narzędzia (narzędzi).

Wymuszanie jawnych ścieżek zaufania w kodzie nie ma żadnego znaczenia, a we wszystkich przypadkach to, co widzę po włączeniu debugowania (za pomocą zestawu właściwości Jawax.net.debug do "wszystkich") to:

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 

Niestety nie mogę pozwolić na przesłonięcie czeku przez wdrożenie własnego TrustManagera - musi to sprawdzić.

Certyfikat, który otrzymuję od hosta, ma kilka rozszerzeń (dokładnie po 9), co sprawia, że ​​zastanawiam się, czy są one częścią tego problemu.

Co jeszcze mogę sprawdzić/spróbować? Przejdź na inną wersję JRE?

+0

Dlaczego nie możesz zaimplementować własnego 'TrustManager'? Nadal możesz korzystać z certyfikatów w menedżerze powierniczym i wykonać czek. –

+0

@Vivin - Podstawowym problemem, który próbuję rozwiązać, jest warstwa aplikacji, której nie kontroluję bezpośrednio (w tym przypadku aplikacja Grails wywołująca niektóre usługi internetowe, które ostatnio stały się tylko HTTPS), więc gdybym mogła z niego korzystać w moim własnym lokalnym scenariuszu nie mogę wymusić jego wykorzystania na początku. To jest ból, ale poradzę sobie z nim później - na razie, jeśli mogę sprawić, że będzie działał na najbardziej szczegółowym poziomie, przynajmniej powie mi, gdzie pójść dalej w łańcuchu rozdzielczości. – Bill

+0

Czy używasz tego na serwerze aplikacji, takim jak GlassFish? Możesz sprawdzić, czy proces uruchamia plik binarny Java w pliku /usr/lib/jvm/java-1.6.0-sun-1.6.0.20.x86_64, a nie inne pliki binarne Java, które nie odpowiadają Twojemu zaufanemu magazynowi (OpenJDK, itp). Opublikowane wyniki debugowania są pomocne, możesz dodać więcej? – Brad

Odpowiedz

0

Możesz nadal sprawdzić certyfikat, wdrażając własnego menedżera zaufania. Wpadłem na podobny problem: here. Próbowałem też dodać certyfikat do cacerts, ale bezskutecznie.

W menedżerze powierniczym należy jawnie załadować certyfikaty.Zasadniczo to, co musiałem zrobić, było coś takiego:

Najpierw tworzę menedżera zaufania, który wykorzystuje rzeczywiste pliki certyfikatu:

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

Teraz, za pomocą tego menedżera zaufania, musiałem stworzyć fabrykę gniazda:

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

Wtedy właśnie zarejestrowany nowy protokół:

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

jedynym wadą jest to, że musiałem stworzyć określony protokół (vhttps) dla tego konkretnego certyfikatu.

+0

Trudno uwierzyć we wszystko, co było naprawdę konieczne. Istnieje wyraźny smak niezrozumienia podstawowego problemu związanego z tym wszystkim. – EJP

+0

@EJP Nie trzeba być tak protekcjonalnym. Mam świadomość, że to rozwiązanie nie jest optymalne. Jeśli masz lepsze rozwiązanie, cieszę się, że to słyszę. Próbowałem wielu rzeczy, aby to działało, łącznie z dodaniem certyfikatu (upewniłem się nawet, że udało mi się prześledzić cały łańcuch zaufania) do pliku cacerts serwera. Nic nie działało. Badałem ten problem, zanim w końcu skorzystałem z tego rozwiązania. To nie był mój pierwszy wybór. –

+0

@Vivin - Doceniam sugestię, ale mam nadzieję, że nie muszę iść tą drogą. :) Wiele innych kodów, których nie kontroluję bezpośrednio, musiałoby zostać zmienionych, więc jest to jeden z tych scenariuszy, w których mam nadzieję, że gdzieś po mojej stronie lub stronie konfiguracji komputera jest błąd. – Bill

0

Moje przypuszczenie jest jedną z tych rzeczy się stało:

a) uruchomić kod na serwerze WWW. Często używają własnego magazynu zaufania - czy jesteś naprawdę pewien, że jest on używany, gdy twój kod jest wykonywany?

b) Domyślnie Java spróbuje sprawdzić ważność certyfikatów, pobierając i interpretując listy CRL. Jeśli jesteś za serwerem proxy, pobieranie nie powiedzie się, w wyniku czego cała kontrola PKIX zakończy się niepowodzeniem.

+1

Gdy instaluje certyfikat serwera bezpośrednio, (b) nie ma zastosowania. Dobra odpowiedź jest inna. – EJP

+0

@EJP Ups, masz rację, pomieszałem to z importowaniem certyfikatu głównego. Naprawię to. – emboss

+0

Na początku myślałem, że A było to, a potem, gdy sprowadziłem go do małego przykładowego kodu z pełnymi debugami i nadal nie udało mi się, byłem zbulwersowany. Docenić jednak sugestie. – Bill

0

Śledzenie debugowania SSL pokaże, którego pliku cacerts używasz, o ile nie załadujesz go samodzielnie. Najwyraźniej nie używasz tego, kim myślisz, że jesteś.

+0

Dobra myśl, aby sprawdzić, ale nie w tym przypadku: "trustStore to: /usr/lib/jvm/java-1.6.0-sun-1.6.0.20.x86_64/jre/lib/security/jssecacerts". Jak już wspomniałem, próbowałem go zarówno z jssecacerts jak i cacerts, co obejmuje wymuszanie ścieżki przez ustawienia trustStore. – Bill

Powiązane problemy