3

証明書のホスト名/CNがURLのホスト名と一致するかどうかを確認する関数を書いています。

私の設定: Javaが提供する デフォルトのSSLSocketを使用しています。SSLSocketにHandshakeCompletedListenerを追加しました。(これは私が解決策を提案しているだけです。ハンドシェイクが完了した後に証明書を検証するための最良の方法ではないと思います)

私の難問:https ://gmail.com、つまりホスト:gmail.comポート:443 over ssl に接続すると、CN=mail.google.comの証明書を取得します。ホスト名検証機能はこの証明書を拒否し、接続を閉じます。

不思議なことに、広く使用されているブラウザは同じことをしません。「証明書は信頼されていません。続行しますか?」という通常のメッセージは表示されません。どういうわけか、URLのホスト名と一致していなくても、提示された証明書をすべて信頼しています。

では、証明書を完全に拒否しないように、ブラウザは何をしているのでしょうか。証明書が途中で有効になるようにするために、どのような追加の手順を実行していますか?つまり、一連のリダイレクトの後、 https://gmail.comはhttps://mail.google.comに置き換えられ、証明書はCN = mail.google.comと一致するため、問題なく検証されます。このメカニズムの背後にある特定のルールはありますか?

ご意見をお聞かせください。:)

編集:ホスト/ピアから送信された証明書を印刷するテストプログラムを含めました。また、httpメッセージを出力します。プログラムは、getリクエストをgmail.comに送信します。証明書にCN=mail.google.comとしてCNが表示されます。誰かがこれをテストする気がありますか?iancurl -v -k https://gmail.comがコメントで示唆しているように、まったく異なる結果を返すため、この動作は奇妙だと思います。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.cert.Certificate;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;


public class SSLCheck {

public static String[] supportedCiphers = {"SSL_RSA_WITH_RC4_128_MD5",
                                        "SSL_RSA_WITH_RC4_128_SHA",
                                        "TLS_RSA_WITH_AES_128_CBC_SHA",
                                        "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                                        "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                                        "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
                                        "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
                                        "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
                                        "SSL_RSA_WITH_DES_CBC_SHA",
                                        "SSL_DHE_RSA_WITH_DES_CBC_SHA",
                                        "SSL_DHE_DSS_WITH_DES_CBC_SHA",
                                        "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
                                        "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
                                        "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
                                        "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
                                        "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"};

public static void main(String[] args) {
    int port = 443;
    String host = "gmail.com";

    try {
        Socket sock = SSLSocketFactory.getDefault().createSocket(host, port);
        sock.setSoTimeout(2000);
        ((SSLSocket)sock).setEnabledCipherSuites(supportedCiphers);

        PrintWriter out = new PrintWriter(new OutputStreamWriter(sock.getOutputStream()));

        out.println("GET " + "/mail" + " HTTP/1.1");
        out.println("Host: "+host);
        out.println("Accept: */*");
        out.println();
        out.flush();

        BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));

        SSLSocket ssls = (SSLSocket)sock;
        Certificate[] peercerts = ssls.getSession().getPeerCertificates();

        System.out.println("***********************PEER CERTS**********************");
        for(int i=0;i<peercerts.length;i++){
            System.out.println(peercerts[i]);       
            System.out.println("*********************************************************");
        }

        String line;
        while ((line = in.readLine())!=null) {
            System.out.println(line);
        }

        out.close();
        in.close();

    } catch (UnknownHostException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally { 
        System.exit(0);
    }
}

}
4

2 に答える 2

3

Server Name Indication拡張機能を使用せずに取得する証明書は、実際にはCN=mail.google.com、のサブジェクト代替名なしのですgmail.com

SNIを試してみると、次のような別の証明書を取得できますgmail.com

openssl s_client -connect gmail.com:443 -servername gmail.com | openssl x509 -text -noout

デスクトップ上の最新バージョンのブラウザはSNIをサポートしています。デスクトップでは、SNIをサポートしていない主な残りの1つは、どのバージョンのXPのIEでもあります。これはSSLv3にバックポートされていないTLS拡張であるため、これは、で機能しない理由も説明していcurl -3ます。

JavaでのSNIのサポートは、Java 7以降(およびクライアント側でのみ)のみ利用可能です。

(ちなみに、何をしているのかわからない限り、デフォルトで有効になっている暗号スイートを残してください。特に、のような弱いものや、のようなEXPORT疑似スイートを有効にしないでくださいTLS_EMPTY_RENEGOTIATION_INFO_SCSV。)

于 2012-09-13T17:41:38.170 に答える
2

の簡単なテストでcurl -vは、への最初の接続https://gmail.comCN=gmail.com。SSLエンベロープ内のHTTP応答は、への301リダイレクトでhttps://mail.google.com/mail/あり、これにより証明書が提示されCN=mail.google.comます。したがって、この場合、CN実際にはすべての段階で一致します。

ただし、一般に、「サブジェクト代替名」拡張について学習する必要があります。これは、証明書がメインCNに加えて有効ないくつかの異なるホスト名を指定する方法です。

于 2012-09-12T23:08:16.133 に答える