Search code examples
javassltruststore

Why do I get a certificate error with HttpURLConnection but not with SSLSocket and how do make the HTTPS call work?


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?


Solution

  • 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.