我正在尝试使用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
答案 0 :(得分:57)
您尝试连接的服务器的证书看起来与其主机名不匹配。
当HTTPS客户端连接到服务器时,它会验证证书中的主机名是否与服务器的主机名匹配。证书不可靠,它必须匹配您想要与之交谈的服务器。 (作为类比,即使您相信护照是合法的,您仍然必须检查它是否是您想要与之交谈的人,而不仅仅是您认为合法的护照。)
在HTTP中,这可以通过检查:
来完成证书包含与主机名匹配的DNS主题备用名称(这是标准扩展名);
如果失败,主题专有名称的最后一个CN(如果需要,这是主名称)与主机名匹配。 (参见RFC 2818。)
如果没有证书,很难说出主题替代名称是什么(尽管如果您连接浏览器并查看其内容的详细信息,您应该能够看到它。) 主题专有名称似乎是:
EMAILADDRESS=info@plesk.com, CN=plesk, OU=Plesk, O=Parallels, L=Herndon, ST=Virginia, C=US
(因此,如果你没有DNS的主题替代名称,那么它需要是CN = ssl.someUrl.de而不是CN = plesk:ssl.someUrl.de已经;我的猜测是你不要吨。)
您可以使用HttpsURLConnection.setHostnameVerifier(..)绕过主机名验证。编写一个自定义的HostnameVerifier并不太难以通过验证验证,尽管我建议只在证书与此处有关的证书时才这样做。您应该能够使用SSLSession参数及其getPeerCertificates()方法获取它。
(另外,你不需要按照你的方式设置javax.net.ssl。*属性,因为你仍然使用默认值。)
或者,如果您可以控制要连接的服务器及其证书,则可以创建与上述命名规则相匹配的证书(CN应该足够,但主题替代名称是一种改进)。如果自签名证书足以满足您的要求,请确保其公用名(CN)是您尝试与之通信的主机名(没有完整的URL,只有主机名)。
答案 1 :(得分:22)
在Java 8中,您可以使用以下代码跳过服务器名称检查:
HttpsURLConnection.setDefaultHostnameVerifier ((hostname, session) -> true);
但是这只应该用于开发!
答案 2 :(得分:13)
我创建了一个方法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);
}
答案 3 :(得分:3)
我在这里找到了一个很好的解决方案: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一样解决。
答案 4 :(得分:1)
服务器名称应与创建证书时提供的名/姓相同
答案 5 :(得分:1)
如果您要查找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.endpoint.identification.algorithm的默认值已更改为https,它将执行主机名验证(否则可能会发生中间人攻击)。将ssl.endpoint.identification.algorithm设置为空字符串以恢复以前的行为。 Apache Kafka Notable changes in 2.0.0
解决方案: SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG,“”
答案 6 :(得分:0)
以防万一:
用例:我正在使用自签名证书进行本地主机上的开发。
错误:原因:java.security.cert.CertificateException:未找到与本地主机匹配的名称
解决方案: 生成自签名证书时,请确保您回答这样的问题(有关原因,请参见Bruno的回答):
What is your first and last name?
[Unknown]: localhost
作为奖励,这是我的步骤:
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
...
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
干杯
答案 7 :(得分:0)
可以通过设置 VM 属性跳过主机名验证:
-Djdk.internal.httpclient.disableHostnameVerification
这适用于 Java 11 HttpClient 实现。