我正在编写一个函数来验证证书中的主机名/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);
}
}
}