Wie rufe ich ein nicht vertrauenswürdiges SSL-Serverzertifikat ab, um es zu überprüfen und ihm zu vertrauen?

Mein Problem:

Ich möchte eine Verbindung zu Servern herstellen (die nicht auf das HTTPS-Protokoll beschränkt sind - LDAP über SSL, SMTPS, IMAPS usw.), die möglicherweise Zertifikate verwenden, denen Java standardmäßig nicht vertraut (da dies der Fall ist) selbst signiert).

Der gewünschte Arbeitsablauf besteht darin, die Verbindung herzustellen, die Zertifikatsinformationen abzurufen, diese dem Benutzer vorzulegen und sie, falls er sie akzeptiert, dem Truststore hinzuzufügen, damit sie für die Zukunft als vertrauenswürdig eingestuft werden.

Ich kann das Zertifikat nicht mehr abrufen. Ich habe Code (siehe am Ende des Beitrags), den ich von hier aus und von Websites, auf die durch Antworten auf Fragen zu Java SSL verwiesen wird, abgeschnitten habe. Der Code erstellt einfach eineSSLSocket, startet den SSL-Handshake und fragt die SSL-Sitzung nach demCertificate[]. Der Code funktioniert einwandfrei, wenn ich mit einem bereits vertrauenswürdigen Zertifikat eine Verbindung zu einem Server herstelle. Wenn ich mich aber mit einem selbstsignierten Zertifikat mit einem Server verbinde, erhalte ich das Übliche:

<code>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]
</code>

Wenn ich mit renne-Djavax.net.debug=all Ich sehe, dass die JVM das selbstsignierte Zertifikat abruft, aber die Verbindung für die Verwendung eines nicht vertrauenswürdigen Zertifikats sprengt, bevor sie den Punkt erreicht, an dem die Zertifikate zurückgegeben werden.

Scheint ein Henne-Ei-Problem zu sein. Ich kann die Zertifikate nicht sehen, da sie nicht vertrauenswürdig sind. Aber ich muss die Zertifikate sehen, um sie zum Truststore hinzufügen zu können, damit sie vertrauenswürdig sind. Wie kommst du da raus?

Zum Beispiel, wenn ich das Programm wie folgt ausführe:

<code>java SSLTest www.google.com 443
</code>

Ich erhalte einen Ausdruck der von Google verwendeten Zertifikate. Aber wenn ich es so laufe

<code>java SSLTest my.imap.server 993
</code>

Ich erhalte die oben genannte Ausnahme.

Der Code:

<code>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");
                }
            }
        }
    }
}
</code>

Antworten auf die Frage(2)

Ihre Antwort auf die Frage