2012-05-14 12 views
5

Mój problem:Jak pobrać niezaufany certyfikat serwera SSL, aby go przejrzeć i czym zaufać?

Chcę połączyć się z serwerami (nie ograniczając się do protokołu HTTPS - może to być LDAP-over-SSL, może to być SMTP, może to być IMAPS, itp.), Które może używać certyfikatów, które Java domyślnie nie będzie zaufania (ponieważ są one podpisane przez siebie).

Pożądanym przepływem pracy jest próba nawiązania połączenia, pobranie informacji o certyfikacie, przedstawienie go użytkownikowi, a jeśli to zaakceptuje, dodanie go do magazynu zaufanych certyfikatów, aby był zaufany w przyszłości.

Utknąłem podczas pobierania certyfikatu. Mam kod (patrz na końcu postu), który pobrałem z tego miejsca iz witryn wskazywanych przez odpowiedzi na pytania dotyczące java SSL. Kod tworzy po prostu SSLSocket, rozpoczyna uzgadnianie SSL i prosi o sesję SSL dla Certificate[]. Kod działa poprawnie, gdy łączę się z serwerem przy użyciu zaufanego certyfikatu. Ale kiedy połączyć się z serwerem za pomocą samopodpisany cert otrzymuję zwykle:

Exception in thread "main" 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 
     at sun.security.ssl.Alerts.getSSLException(Unknown Source) 
     at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source) 
     at sun.security.ssl.Handshaker.fatalSE(Unknown Source) 
     at sun.security.ssl.Handshaker.fatalSE(Unknown Source) 
     at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source) 
     [etc] 

Jeśli biegnę z -Djavax.net.debug=all widzę, że JVM nie odzyskać samopodpisany cert ale będzie wysadzić połączenia za korzystanie niezaufany certyfikat przed dotarciem do punktu, w którym zwróci certyfikaty.

Wydaje się, że problem dotyczy kurczaków i jaj. Nie pozwala mi zobaczyć certyfikatów, ponieważ nie są zaufane. Ale muszę zobaczyć certyfikaty, aby móc dodać je do magazynu zaufanych certyfikatów, aby były zaufane. Jak się z tego wyłamujesz?

Na przykład, jeśli uruchomić program jako:

java SSLTest www.google.com 443 

dostaję wydruk CERT Google korzystających. Ale jeśli go uruchomię, otrzymam wyjątek opisany powyżej.

Kod:

import java.io.InputStream; 
import java.io.OutputStream; 
import java.security.cert.*; 
import javax.net.SocketFactory; 
import javax.net.ssl.*; 

public class SSLTest 
{ 
    public static void main(String[] args) throws Exception { 
     if (args.length != 2) { 
      System.err.println("Usage: SSLTest host port"); 
      return; 
     } 

     String host = args[0]; 
     int port = Integer.parseInt(args[1]); 

     SocketFactory factory = SSLSocketFactory.getDefault(); 
     SSLSocket socket = (SSLSocket) factory.createSocket(host, port); 

     socket.startHandshake(); 

     Certificate[] certs = socket.getSession().getPeerCertificates(); 

     System.out.println("Certs retrieved: " + certs.length); 
     for (Certificate cert : certs) { 
      System.out.println("Certificate is: " + cert); 
      if(cert instanceof X509Certificate) { 
       try { 
        ((X509Certificate) cert).checkValidity(); 
        System.out.println("Certificate is active for current date"); 
       } catch(CertificateExpiredException cee) { 
        System.out.println("Certificate is expired"); 
       } 
      } 
     } 
    } 
} 

Odpowiedz

7

Można to zrobić wdrażania tymczasowej TrustManager że akceptuje wszystkie certyfikaty i tymczasowy HostnameVerifier który weryfikuje wszystkie nazwy (oczywiście trzeba używać ich tylko do pobierania certyfikatu i nie wysyłać prywatne dane).

następujący kod pobrać certyfikaty z dowolnego adresu URL https i zapisać je do pliku:

URL url = new URL("https://<yoururl>"); 

SSLContext sslCtx = SSLContext.getInstance("TLS"); 
sslCtx.init(null, new TrustManager[]{ new X509TrustManager() { 

    private X509Certificate[] accepted; 

    @Override 
    public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { 
    } 

    @Override 
    public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { 
     accepted = xcs; 
    } 

    @Override 
    public X509Certificate[] getAcceptedIssuers() { 
     return accepted; 
    } 
}}, null); 

HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); 

connection.setHostnameVerifier(new HostnameVerifier() { 

    @Override 
    public boolean verify(String string, SSLSession ssls) { 
     return true; 
    } 
}); 

connection.setSSLSocketFactory(sslCtx.getSocketFactory()); 

if (connection.getResponseCode() == 200) { 
    Certificate[] certificates = connection.getServerCertificates(); 
    for (int i = 0; i < certificates.length; i++) { 
     Certificate certificate = certificates[i]; 
     File file = new File("/tmp/newcert_" + i + ".crt"); 
     byte[] buf = certificate.getEncoded(); 

     FileOutputStream os = new FileOutputStream(file); 
     os.write(buf); 
     os.close(); 
    } 
} 

connection.disconnect(); 
Powiązane problemy