60

我正在尝试使用 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

4

9 回答 9

78

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

当 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;我猜您没有。)

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

(此外,您不需要按照您所做的方式设置 javax.net.ssl.* 属性,因为无论如何您都在使用默认值。)

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

于 2010-06-22T13:42:15.933 回答
45

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

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

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

于 2014-09-02T16:07:44.590 回答
19

我创建了一个方法 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);
}
于 2015-11-21T18:25:13.440 回答
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 2.0.0 中的显着变化

解决方案:SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG, ""

于 2019-04-08T07:42:42.000 回答
5

我在这里找到了一个很好的解决方案: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 服务托管在 localhost 应用服务器上,它认为应该像 mkyong 在他的文章中描述的那样解决。

于 2013-07-10T04:51:30.680 回答
3

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

错误:原因: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.更新cacerts
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
于 2020-01-31T08:00:29.413 回答
1

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

于 2013-07-25T13:52:56.343 回答
0

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

-Djdk.internal.httpclient.disableHostnameVerification

这适用于 Java 11 HttpClient 实现。

于 2021-04-19T12:51:52.057 回答
0
HttpsURLConnection.setDefaultHostnameVerifier((String hostname, SSLSession sslSession) -> {
        return hostname.equals("localhost");
    });

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

于 2021-09-03T06:50:01.647 回答