【问题标题】:Why do I get "No name matching found" CertificateException?为什么我会收到“未找到名称匹配”CertificateException?
【发布时间】:2015-01-03 17:55:54
【问题描述】:

据我了解,在大多数情况下,此异常表明证书所有者 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 的答案中的How do you configure Apache/Tomcat to trust internal Certificate Authorities for server-to-server https requests 所示。

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 时,它说证书不受信任,我将其添加到浏览器例外列表中并且它工作正常。所以看起来我错误地将这些证书添加到cacerts,因此java无法使用它们。但我可以通过别名或 CN 轻松找到它们:

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

【问题讨论】:

  • 如果它是自签名的,那么它将不被信任,因为层次结构不会一直到 TA
  • @Scary Wombat 但是我将它们都添加到cacerts..并在那里将它们标记为受信任..这还不够吗?这难道不是让自签名证书受信任的方式吗?
  • 你使用了错误的类访问https

标签: java apache tomcat https solaris


【解决方案1】:

最后我要做的就是:

  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);
    
  2. 1234563 ,即在Java: Overriding function to disable SSL certificate check。这段代码替换了我需要的默认SSLSocketFactory。所以我不得不删除所有这些东西,只使用禁用主机名验证的代码。

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

如果我设法避免禁用主机名验证当然会好得多,但我不能。证书可能有问题..

【讨论】:

  • 没有投反对票,但禁用主机名验证几乎总是错误的做法。不建议将其作为解决方案。
【解决方案2】:

这就是我之前在 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);

}   

【讨论】:

    【解决方案3】:

    使用 HttpsURLConnectionSSLSocketFactory 执行 SSL 握手。

    【讨论】:

    • 这对我有什么帮助?
    • 尝试研究HttpsURLConnection。您使用了错误的类来执行 SSL,这就是将证书呈现给服务器的方式。您的代码对证书没有任何作用。如果你使用我建议的课程,它会的。
    • 感谢您将其标记下来,这不是鼓励帮助的好方法!
    • 可能是错误的,但是当目标服务器使用真正的受信任证书时它确实有效......但是,谢谢,我也会考虑一下
    猜你喜欢
    • 2011-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-18
    • 2013-04-22
    • 2017-01-04
    相关资源
    最近更新 更多