Как мне получить ненадежный сертификат SSL-сервера, чтобы проверить его и доверять ему?

Моя проблема:

Я хочу подключиться к серверам (не ограничиваясь протоколом HTTPS — может быть LDAP-over-SSL, может быть SMTPS, может быть IMAPS и т. д.), которые могут быть использование сертификатов, которым Java не будет доверять по умолчанию (поскольку они самозаверяющие).

Желаемый рабочий процесс состоит в том, чтобы попытаться установить соединение, получить информацию о сертификате, представить ее пользователю и, если он примет ее, добавить ее в хранилище доверенных сертификатов, чтобы в дальнейшем ему можно было доверять.

Я не могу получить сертификат. У меня есть код (см. в конце поста), который я взял отсюда и с сайтов, на которые указывают ответы на вопросы о java SSL. Код просто создает SSLSocket, запускает рукопожатие SSL и запрашивает у сеанса SSL сертификат Certificate[]. Код отлично работает, когда я подключаюсь к серверу, используя уже заслуживающий доверия сертификат. Но когда я подключаюсь к серверу с помощью самозаверяющего сертификата, я получаю обычное:

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]

Если я запускаю с -Djavax.net.debug=all, я вижу, что JVM извлекает самозаверяющий сертификат. cert, но взорвет соединение из-за использования ненадежного сертификата, прежде чем дойдет до точки, где он вернет сертификаты.

Похоже на проблему курицы и яйца. Это не позволит мне увидеть сертификаты, потому что им не доверяют. Но мне нужно увидеть сертификаты, чтобы иметь возможность добавить их в хранилище доверенных сертификатов, чтобы им можно было доверять. Как вырваться из этого?

Например, если я запускаю программу как:

java SSLTest www.google.com 443

Я получаю распечатку сертификатов, которые использует Google. Но если я запускаю его как

java SSLTest my.imap.server 993

, я получаю исключение, упомянутое выше.

Код:

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");
                }
            }
        }
    }
}
5
задан QuantumMechanic 14 May 2012 в 02:52
поделиться