英文:
Why do I get a certificate error with HttpURLConnection but not with SSLSocket and how do make the HTTPS call work?
问题
我不理解我从这段代码中得到的结果。
以下是代码:
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
SSLSocketFactory factory = context.getSocketFactory();
try {
System.out.println("Starting SSL handshake...");
SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
socket.setSoTimeout(10000);
socket.startHandshake();
socket.close();
System.out.println("No errors, certificate is already trusted");
} catch (SSLException e) {
e.printStackTrace(System.out);
}
try {
System.out.println("\nStarting an https connection to the same address...");
HttpURLConnection http = (HttpURLConnection)new URL("https://" + host + ":" + port).openConnection();
http.setRequestMethod("POST");
System.out.println("Response code: " + http.getResponseCode());
} catch (SSLException e) {
e.printStackTrace(System.out);
}
以下是结果:
Starting SSL handshake...
No errors, certificate is already trusted
Starting an https connection to the same address...
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching xxxxxxxxxxxx found (NOTE: xxxxxxxxxxx is the value of 'host')
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1640)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1570)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:352)
Caused by: java.security.cert.CertificateException: No name matching dqdev4607vm02 found
at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:231)
at sun.security.util.HostnameChecker.match(HostnameChecker.java:96)
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:459)
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:440)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:209)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1622)
... 14 more
我不明白为什么套接字能够在我的信任存储中找到证书,而https连接却找不到。任何帮助将不胜感激。
英文:
I do not understand the results I am getting from this code.
Here is the code:
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
SSLSocketFactory factory = context.getSocketFactory();
try {
System.out.println("Starting SSL handshake...");
SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
socket.setSoTimeout(10000);
socket.startHandshake();
socket.close();
System.out.println("No errors, certificate is already trusted");
} catch (SSLException e) {
e.printStackTrace(System.out);
}
try {
System.out.println("\nStarting an https connection to the same address...");
HttpURLConnection http = (HttpURLConnection)new URL("https://" + host + ":" + port).openConnection();
http.setRequestMethod("POST");
System.out.println("Response code: " + http.getResponseCode());
} catch (SSLException e) {
e.printStackTrace(System.out);
}
And here are the results:
Starting SSL handshake...
No errors, certificate is already trusted
Starting an https connection to the same address...
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching xxxxxxxxxxxx found (NOTE: xxxxxxxxxxx is the value of 'host')
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1640)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1570)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:352)
Caused by: java.security.cert.CertificateException: No name matching dqdev4607vm02 found
at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:231)
at sun.security.util.HostnameChecker.match(HostnameChecker.java:96)
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:459)
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:440)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:209)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1622)
... 14 more
I do not understand why the socket is finding the cert in my trust-store and the https connection is not. Any help would be appreciated.
OK, based on the answers received so far, here is the explanation of what is happening.
I have extracted the SSL code from a class that I found on the internet years ago to add a new cert to my trust store. If the SSL fails, it downloads the cert and adds it to my store. Then when I run the same class again, the first "try" will pass because the cert is now in my store.
But then when I try to access that exact same site with https, I get the error. In response to "Steffen Ullrich" answer, the host and port are identical. It is not a matter of one having a subdomain the other not.
So the new question is, how do I change the HTTPS call to not throw the error? Could it be that each call to the same server are searching two different trust-stores?
答案1
得分: 1
不明白套接字为什么能在我的信任存储中找到证书,而https连接却不能。任何帮助都将不胜感激。
问题不是在于在信任存储中查找根CA - 这在两种情况下都有效。 HttpURLConnection的问题实际上出现在证书的主机名验证上。在这里,证书的主题/SAN与您在URL中使用的主机名不匹配,例如证书是为 example.org
,但网站的URL中使用的是 whatever.example.org
。
... CertificateException: 找不到匹配的名称 xxxxxxxxxxxx(注意:xxxxxxxxxx是 'host' 的值)
您在SSLSocket中不会收到此错误,因为那里不进行主机名验证。
来自网络协议的安全性 - 关于直接使用SSLSocket的警告:
注意:SSLSocket不执行主机名验证。您的应用程序需要自行执行主机名验证,最好通过使用期望的主机名调用getDefaultHostnameVerifier()来实现。此外,请注意,HostnameVerifier.verify() 在出错时不会引发异常。相反,它返回一个布尔结果,您必须明确检查。
英文:
> I do not understand why the socket is finding the cert in my trust-store and the https connection is not. Any help would be appreciated.
The problem is not about finding the root CA in the trust store - this works in both cases. The problem you get with HttpURLConnection is instead in the hostname validation of the certificate. Here the subject/SAN of the certificate does not match the hostname you use in the URL, i.e. for example the certificate being for example.org
but the site was accessed with whatever.example.org
in the URL.
> ... CertificateException: No name matching xxxxxxxxxxxx found (NOTE: xxxxxxxxxxx is the value of 'host')
You don't get this error with SSLSocket since no hostname validation is done there.
From Security with network protocols - Warnings about using SSLSocket directly:
> Caution: SSLSocket does not perform hostname verification. It is up to your app to do its own hostname verification, preferably by calling getDefaultHostnameVerifier() with the expected hostname. Also, be aware that HostnameVerifier.verify() doesn't throw an exception on error. Instead, it returns a boolean result that you must explicitly check.
答案2
得分: -1
SSL是一种加密协议。HTTPS是在SSL上使用HTTP的方式,它将HTTP数据嵌入到SSL数据中。
如果你想使用HTTPS,就不需要直接使用SSL。标准的HTTP库都支持HTTPS,如果URL的协议是https,它们会自动使用它。
英文:
SSL is an encryption protocol. HTTPS is using HTTP over SSL- it's embedding the HTTP data as the payload to the SSL data.
If you want to use HTTPS, you wouldn't use SSL directly. The standard HTTP libraries all support https, they will automatically use it if the url's scheme is https.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论