为什么我会收到“未找到名称匹配”CertificateException?

hor*_*rgh 6 java apache https tomcat solaris

据我了解,在大多数情况下,此异常表明证书所有者 CN(公用名)与 url 中的主机名不匹配。然而就我而言,它们确实匹配,但异常仍然出现。

远程服务器证书层次结构是:

  1. 自签名证书CN=sms.main.ru
  2. 由第一个签名的证书和CN=client.sms.main.ru

我的 java 客户端在 apache-tomcat 6 下启动并尝试连接https://client.sms.main.ru/并抛出以下异常:

No name matching client.sms.main.ru found
Run Code Online (Sandbox Code Playgroud)

这两个证书都添加到$JAVA_HOME/jre/lib/security/cacertsvia中,如unixtippse 的答案中的How do you configure Apache/Tomcat to trust服务器到服务器 https 请求的内部证书颁发机构$JAVA_HOME/bin/keytool所示。

Java 代码非常简单:

URL url = new URL(strurl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Connection", "close");
con.setDoOutput(true);
con.connect();
Run Code Online (Sandbox Code Playgroud)

我缺少什么?


有趣的是,当我尝试在 Windows PC 上使用浏览器访问此 url 时,它说该证书不受信任,我将其添加到浏览器例外列表中,并且工作正常。所以看起来我cacerts错误地添加了这些证书,因此 java 无法使用它们。但我可以通过别名或 CN 轻松找到它们:

$JAVA_HOME/bin/keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts | less
Run Code Online (Sandbox Code Playgroud)

hor*_*rgh 7

最后我所要做的就是:

  1. 禁用主机名验证:

    // Create all-trusting host name verifier
    HostnameVerifier allHostsValid = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };
    // Install the all-trusting host verifier
    HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 确保cacerts使用默认文件,因为在研究时我尝试禁用 ssl 证书验证并使用可以在 SO 上的大量线程中找到的代码(实现自己的X509TrustManager),即在Java 中:覆盖函数以禁用 SSL 证书检查。这段代码替换了SSLSocketFactory我需要的 default 。因此,我必须删除所有这些内容并仅使用禁用主机名验证的代码。

我离开了HttpURLConnection。无需将其替换为HttpsURLConnection.

如果我也设法避免禁用主机名验证,那肯定会好得多,但我做不到。可能是证书有问题。。