为什么我会收到"No name matching found"证书异常?

6
据我所知,大多数情况下这个异常表示证书所有者的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

这两个证书都通过$JAVA_HOME/bin/keytool添加到$JAVA_HOME/jre/lib/security/cacerts,如unixtippse的回答中所示,用于配置Apache/Tomcat信任内部证书颁发机构以进行服务器间的https请求。

Java代码非常简单:

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

我错过了什么?


有趣的是,当我尝试在Windows PC上使用浏览器访问此URL时,它会提示证书不受信任,我将其添加到浏览器的异常列表中,然后就可以正常工作了。所以看起来我没有正确地将这些证书添加到中,导致Java无法使用它们。但我可以很容易地通过别名或CN找到它们:

$JAVA_HOME/bin/keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts | less

如果它是自签名的,则不会被信任,因为层次结构不会一直延伸到TA。 - Scary Wombat
@Scary Wombat 但我将它们都添加到了cacerts中,并在那里标记为受信任的。这不够吗?这不是让自签名证书得到信任的正确方法吗? - horgh
您正在使用错误的类来访问https。 - CharlieS
3个回答

7
最终我需要做的只有:

  1. Disable hostname verification:

    // 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);
    
  2. Make sure that the default cacerts file is used, as while researching I tried to disable ssl certificate verification and used code which can be found in a great number of threads on SO (implementing own X509TrustManager), i.e. in Java: Overriding function to disable SSL certificate check. This code replaced default SSLSocketFactory, which I needed. So I had to delete all this stuff and use code disabling hostname verification only.

我放弃了使用 HttpURLConnection,不需要替换成 HttpsURLConnection

如果我能避免禁用主机名验证肯定会更好,但我没法做到。可能证书出了问题。


我没有点踩,但禁用主机名验证几乎总是错误的做法。不应该将其作为解决方案推荐。 - eis

1

这是我之前在Tomcat上信任自签名证书的方法。

static private SSLSocketFactory newSslSocketFactory(String jksFile, char[] password) {

    try {
        KeyStore trusted = KeyStore.getInstance("JKS");
        InputStream in = StaticHttpsClient.class.getClassLoader().getResourceAsStream(jksFile);
        try {
            trusted.load(in, password);
        } finally {
            in.close();
        }

        SSLSocketFactory socketFactory = new SSLSocketFactory(trusted);
        HostnameVerifier hostnameVerifier =    org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
        socketFactory .setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
        return socketFactory;
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

static protected ClientConnectionManager createClientConnectionManager(String jksFile, char[] password) {
    SchemeRegistry registry = new SchemeRegistry();

    registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
    registry.register(new Scheme("https", 443, newSslSocketFactory(jksFile, password)));
    return new SingleClientConnManager(registry);

}   

-5
使用HttpsURLConnectionSSLSocketFactory执行SSL握手。

这怎么能帮助我呢? - horgh
尝试研究HttpsURLConnection。您正在使用错误的类执行SSL,这是证书向服务器呈现的方式。您的代码没有处理证书。如果您使用我建议的类,则会这样做。 - CharlieS
谢谢您标记它,这不是鼓励寻求帮助的好方式! - CharlieS
可能这样做是错误的,但当目标服务器使用真正可信的证书时它确实有效...不过还是谢谢,我也会再考虑一下。 - horgh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接