3

我正在编写一个函数来验证证书中的主机名/CN 是否与 url 中的主机名匹配。

我的设置: 我正在使用Java 提供的默认SSLSockets 。我在 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 消息。该程序向 gmail.com 发送一个获取请求。我仍然在证书中看到 CN=mail.google.com。有人愿意测试一下吗?我觉得这种行为很奇怪curl -v -k https://gmail.com,因为正如ian在他的评论中所建议的,它返回了一个完全不同的结果。

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 扩展的情况下获得的证书确实是 for CN=mail.google.com,没有任何 Subject Alternative Name for gmail.com

如果您尝试使用 SNI,您将获得不同的证书,用于gmail.com

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

桌面上大多数现代版本的浏览器都支持 SNI。在桌面上,剩下的主要不支持 SNI 的是任何版本的 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.com提供了一个带有CN=gmail.com. SSL 信封内的 HTTP 响应是一个 301 重定向https://mail.google.com/mail/,它反过来提供一个CN=mail.google.com证书。所以在这种情况下,CN实际上在每个阶段都匹配

但一般来说,您需要了解“主题备用名称”扩展,这是一种证书指定多个不同主机名的方式,这些主机名在主 CN 之外有效。

于 2012-09-12T23:08:16.133 回答