【问题标题】:CertificateException: No name matching ssl.someUrl.de foundCertificateException:找不到与 ssl.someUrl.de 匹配的名称
【发布时间】:2011-03-06 19:21:19
【问题描述】:

我正在尝试使用 Java 通过 ssl 连接到我的一台服务器。我尝试了很多选项,这是我最好的尝试:

我使用推荐脚本生成 jssecacerts:http://blogs.oracle.com/andreas/resource/InstallCert.java 使用命令:java InstallCert ssl.someUrl.de changeit

在此之后我第二次执行命令:

Loading KeyStore jssecacerts...
Opening connection to ssl.someUrl.de:443...
Starting SSL handshake...

No errors, certificate is already trusted

Server sent 1 certificate(s):

 1 Subject EMAILADDRESS=info@plesk.com, CN=plesk, OU=Plesk, O=Parallels, L=Hernd
on, ST=Virginia, C=US
   Issuer  EMAILADDRESS=info@plesk.com, CN=plesk, OU=Plesk, O=Parallels, L=Hernd
on, ST=Virginia, C=US
   sha1    f1 0d 2c 54 05 e1 32 19 a0 52 5e e1 81 6c a3 a5 83 0d dd 67
   md5     f0 b3 be 5e 5f 6e 90 d1 bc 57 7a b2 81 ce 7d 3d

Enter certificate to add to trusted keystore or 'q' to quit: [1]

我将文件复制到默认目录并在 Java trustStore 中加载了证书

System.setProperty("javax.net.ssl.trustStore", "C:\\Program Files (x86)\\Java\\jre6\\lib\\security\\jssecacerts");
System.setProperty("javax.net.ssl.trustStorePassword","changeit");

然后我尝试连接

URL url = new URL("https://ssl.someUrl.de/");
URLConnection conn = url.openConnection();
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));

我在第 3 行得到错误:(找不到与 ssl.someUrl.de 匹配的名称)

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching ssl.someUrl.de found

这是默认 plesk 证书的原因还是其他问题?

设置:JRE 6.20、Netbeans 6.8、Windows7 64bit

【问题讨论】:

    标签: java ssl plesk


    【解决方案1】:

    您尝试连接的服务器的证书似乎与其主机名不匹配。

    当 HTTPS 客户端连接到服务器时,它会验证证书中的主机名是否与服务器的主机名匹配。仅信任证书是不够的,它还必须与您要与之交谈的服务器匹配。 (打个比方,即使您相信护照是合法的,您仍然需要检查它是否是您想与之交谈的人的护照,而不仅仅是您认为合法的任何护照。)

    在 HTTP 中,这是通过检查:

    • 证书包含与主机名匹配的 DNS 主题备用名称(这是标准扩展名)条目;

    • 否则,您的主题可分辨名称的最后一个 CN(如果需要,这是主名称)与主机名匹配。 (参见 RFC 2818。)

    如果没有证书,很难判断主题备用名称是什么(尽管,如果您连接浏览器并更详细地检查其内容,您应该能够看到它。) 主题专有名称似乎是:

    EMAILADDRESS=info@plesk.com, CN=plesk, OU=Plesk, O=Parallels, L=Herndon, ST=Virginia, C=US
    

    (因此,如果您还没有带有 DNS:ssl.someUrl.de 的主题替代名称,则它需要是 CN=ssl.someUrl.de 而不是 CN=plesk;我猜你没有t.)

    您可以使用HttpsURLConnection.setHostnameVerifier(..) 绕过主机名验证。编写一个绕过验证的自定义 HostnameVerifier 应该不会太难,尽管我建议仅在证书是此处特别关注的证书时才这样做。您应该能够使用 SSLSession 参数及其 getPeerCertificates() 方法来获得它。

    (此外,您不需要像以前那样设置 javax.net.ssl.* 属性,因为无论如何您都在使用默认值。)

    或者,如果您可以控制要连接的服务器及其证书,则可以创建一个符合上述命名规则的证书(CN 应该足够了,尽管主题备用名称是一种改进)。如果自签名证书足以满足您的命名要求,请确保其公用名 (CN) 是您尝试与之通信的主机名(不是完整的 URL,只有主机名)。

    【讨论】:

    • 谢谢我用 HostnameVerifier 做到了。我只需要一个加密连接。只有一个客户端和一个服务器,一个固定的htts://地址。
    • 请记住,您需要某种形式的加密身份验证以提供安全性。否则,这就像与您不认识的人交换秘密一样:保密方法再好,也不能真正保护您。
    • 但我知道服务器和唯一的客户端(在我的计算机上),没有人可以读取数据流。并且 https 页面也受到简单的 htaccess 保护。还是我弄错了?
    • 嗯,这取决于您如何确定没有人可以读取数据流。如果您确实觉得需要 SSL 来保护通信,那么您确实需要检查远程证书是否是您要与之通信的证书。像您正在做的那样手动创建异常是可以的,但您仍然需要检查它是否确实是您想要创建的异常。
    • @Bruno 我正在尝试使用 localhost 在 kafka 中安装 ssl。 localhost 可以工作吗?如果我有我要安装证书的系统的 IP 地址会怎样。
    【解决方案2】:

    我在这里找到了一个很好的解决方案:http://www.mkyong.com/webservices/jax-ws/java-security-cert-certificateexception-no-name-matching-localhost-found/

    但我的问题有点不同,解决方法也不同。

    Web 服务位于远程主机上。 例如:https://some.remote.host/MyWebService?wsdl

    但它只能通过 IP 对任何客户端可用,但证书是为域创建的:some.remote.host (CN=some.remote.host)。并且这个域不能被 IP 解析,因为它没有出现在 DNS 中)。

    所以出现了同样的问题:如果我使用 IP 通过 ssl 连接到 Web 服务,因为证书 CN=some.remote.host 并且它不等于我指定的主机名(即主机 IP)。

    我已通过将此主机名与 /etc/hosts 文件中的 IP 匹配来解决此问题。 问题已解决。

    但如果 Web Service 托管在 localhost 应用服务器上,它认为应该像 mkyong 在他的文章中描述的那样解决。

    【讨论】:

      【解决方案3】:

      服务器名称应与您在创建证书时提供的名字/姓氏相同

      【讨论】:

        【解决方案4】:

        在 Java 8 中,您可以使用以下代码跳过服务器名称检查:

        HttpsURLConnection.setDefaultHostnameVerifier ((hostname, session) -> true);
        

        但是这应该只在开发中使用!

        【讨论】:

        • 太好了,谢谢。更多帮助:mkyong.com/webservices/jax-ws/…
        • @OhadR 有趣的回答没有用,但你分享的链接有效。我很困惑。这需要以静态方式实现吗?
        【解决方案5】:

        我创建了一个方法 fixUntrustCertificate(),因此当我处理不在受信任 CA 中的域时,您可以在请求之前调用该方法。此代码将在 java1.4 之后运行。此方法适用于所有主机:

        public void fixUntrustCertificate() throws KeyManagementException, NoSuchAlgorithmException{
        
        
                TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
        
                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                        }
        
                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                        }
        
                    }
                };
        
                SSLContext sc = SSLContext.getInstance("SSL");
                sc.init(null, trustAllCerts, new java.security.SecureRandom());
                HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        
                HostnameVerifier allHostsValid = new HostnameVerifier() {
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                };
        
                // set the  allTrusting verifier
                HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
        }
        

        【讨论】:

        • 我设置了反向代理来调用另一个 https 服务,这个解决方案解决了我的所有问题。使用其他解决方案,除了 java.security.cert.CertificateException: No name matching imobileqaf.mobile.medcity.net found 之外,其他的异常稍后会出现,例如 sun.security.provider.certpath.SunCertPathBuilderException: cannot find valid certificate请求目标的路径和 java.security.InvalidAlgorithmParameterException:trustAnchors 参数必须非空。
        • 我很高兴知道,我很久以前就使用过这种方法。对于生产环境来说,拥有一个有效的签名证书总是更好,尽管自签名证书总比没有好!
        【解决方案6】:

        如果您正在寻找 Kafka 错误,这可能是因为 Kafka 的版本从 1.x 升级到 2.x。

        javax.net.ssl.SSLHandshakeException: 一般 SSLEngine 问题 ... javax.net.ssl.SSLHandshakeException: 一般 SSLEngine 问题 ... java.security.cert.CertificateException: 找不到名称匹配 ***

        [Producer clientId=producer-1] 与节点 -2 的连接身份验证失败,原因是:SSL 握手失败

        ssl.endpoint.identification.algorithm 的默认值已更改为 https,它执行主机名验证(否则可能发生中间人攻击)。将 ssl.endpoint.identification.algorithm 设置为空字符串以恢复之前的行为。 Apache Kafka Notable changes in 2.0.0

        解决方案: SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, ""

        【讨论】:

        • 所以我今天整天都在为此苦苦挣扎,我已经将 ssl.endpoint.identification.algorithm 添加到 server.properties 但每次我想使用消费者或生产者时,即 ./kafka -console-producer.sh --broker-list fs:9094 --topic test --producer.config ../config/producer.properties 当然还有客户端 jks 的 consumer.properties 上的 ssl 属性等我仍然是得到一个错误,所以我决定把上面的 ssl.endpoint.identification.algorithm 扔到 consumer.properties 上,它似乎已经修复了。
        • 感谢您发布此内容。由于卡夫卡问题,我可以在这里...
        【解决方案7】:

        用例:我在本地主机上使用自签名证书进行开发。

        错误:原因:java.security.cert.CertificateException:找不到与本地主机匹配的名称

        解决方案: 当您生成自签名证书时,请务必这样回答这个问题:

        What is your first and last name?
          [Unknown]:  localhost
        
        //The rest you can fill accordingly
        


        作为奖励,这是我的步骤:

        1.生成自签名证书:

        keytool -genkeypair -alias netty -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 4000
        Enter keystore password: ***
        Re-enter new password: ***
        What is your first and last name?
          [Unknown]:  localhost
        
        //The rest you can fill accordingly
        

        2。复制 src/main/resources 中的证书(如有必要)

        3.更新证书
        keytool -v -importkeystore -srckeystore keystore.p12 -srcstoretype pkcs12 -destkeystore "%JAVA_HOME%\jre\lib\security\cacerts" -deststoretype jks

        4.更新您的配置(在我的情况下为 application.properties):

        server.port=8443
        server.ssl.key-store=classpath:keystore.p12
        server.ssl.key-store-password=jumping_monkey
        server.ssl.key-store-type=pkcs12
        server.ssl.key-alias=netty
        

        【讨论】:

          【解决方案8】:

          可以通过设置虚拟机属性来跳过主机名验证:

          -Djdk.internal.httpclient.disableHostnameVerification

          这适用于 Java 11 HttpClient 实现。

          【讨论】:

          • 给 Java 8 一个机会,但失败了。 :)
          【解决方案9】:
          HttpsURLConnection.setDefaultHostnameVerifier((String hostname, SSLSession sslSession) -> {
                  return hostname.equals("localhost");
              });
          

          将此添加到您调用服务器的方法中。

          【讨论】:

            猜你喜欢
            • 2018-04-15
            • 2015-01-03
            • 2015-05-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多