6

私の問題:

Javaがデフォルトで信頼しない証明書を使用している可能性のあるサーバー(HTTPSプロトコルに限定されない-LDAP-over-SSL、SMTPS、IMAPSなど)に接続したい自己署名)。

必要なワークフローは、接続を試行し、証明書情報を取得してユーザーに提示し、ユーザーがそれを受け入れた場合は、それをトラストストアに追加して、今後信頼されるようにすることです。

証明書の取得に行き詰まっています。私はここから、そして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は自己署名証明書を取得しますが、証明書を返すポイントに到達する前に、信頼できない証明書を使用するための接続を爆破します。

鶏が先か卵が先かという問題のようです。証明書は信頼されていないため、表示されません。ただし、証明書をトラストストアに追加して信頼できるようにするには、証明書を確認する必要があります。どのようにこれから抜け出しますか?

たとえば、プログラムを次のように実行すると、次のようになります。

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

2 に答える 2

14

TrustManagerこれは、すべての証明書を受け入れる一時的なものと、すべての名前を検証する一時的なものを実装して行うことができますHostnameVerifier(明らかに、証明書を取得するためだけに使用し、プライベートデータを送信するためには使用しないでください)。

次のコードは、任意のhttps URLから証明書を取得し、それらをファイルに保存します。

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();
于 2016-05-06T07:37:25.713 に答える
2

AndreasSterbenzのInstallCertユーティリティのこのコピーを参照してください。

于 2012-05-14T07:05:43.120 に答える