Вопрос по java, ssl, ssl-certificate – Как получить сертификат ненадежного SSL-сервера для проверки и доверия к нему?

5

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

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

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

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

<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>

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

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

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

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

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

<code>java SSLTest my.imap.server 993
</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>

Ваш Ответ

2   ответа
10

TrustManager который принимает все сертификаты и временныйHostnameVerifier, который проверяет все имена (очевидно, вы должны использовать их только для получения сертификата, а не для отправки личных данных).

Следующий код извлекает сертификаты из произвольного URL-адреса https и сохраняет их в файл:

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();
2
Благодарность! Как раз то, что я искал. И для записи вот консольная версия того же самого: Code.google.com / р / ява-потребительные примеры / источник / просмотр / багажник / SRC / COM / ... QuantumMechanic

Похожие вопросы