When you want to establish an SSL connection like this;
URL url = new URL("https://localhost:9443/soap_rpc"); |
You may get an exception like this;
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching localhost found at com.sun.net.ssl.internal.ssl.Alerts.getSSLException ( Alerts.java: 174 ) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal ( SSLSocketImpl.java: 1591 ) at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE ( Handshaker.java: 187 ) at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE ( Handshaker.java: 181 ) at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate ( ClientHandshaker.java: 975 ) at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage ( ClientHandshaker.java: 123 ) at com.sun.net.ssl.internal.ssl.Handshaker.processLoop ( Handshaker.java: 516 ) at com.sun.net.ssl.internal.ssl.Handshaker.process_record ( Handshaker.java: 454 ) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord ( SSLSocketImpl.java: 884 ) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake ( SSLSocketImpl.java: 1096 ) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake ( SSLSocketImpl.java: 1123 ) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake ( SSLSocketImpl.java: 1107 ) at sun.net.www.protocol.https.HttpsClient.afterConnect ( HttpsClient.java: 405 ) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect ( AbstractDelegateHttpsURLConnection.java: 166 ) at sun.net.www.protocol.http.HttpURLConnection.getOutputStream ( HttpURLConnection.java: 832 ) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream ( HttpsURLConnectionImpl.java: 230 ) at SSLPost.test ( SSLPost.java: 74 ) at SSLPost.<init> ( SSLPost.java: 29 ) at SSLPost.main ( SSLPost.java: 98 ) Caused by: java.security.cert.CertificateException: No name matching localhost found at sun.security.util.HostnameChecker.matchDNS ( HostnameChecker.java: 210 ) at sun.security.util.HostnameChecker.match ( HostnameChecker.java: 77 ) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity ( X509TrustManagerImpl.java: 264 ) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted ( X509TrustManagerImpl.java: 250 ) at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate ( ClientHandshaker.java: 954 ) ... 14 more |
But, you have installed the server certificate, generated keystore and all work fine. So, what may be the problem?
Let's come to the solution;
While making an SSL connection, HttpsClient steps in and does basic server authentication to protect against URL spoofing which includes verifying that the name of the server is found in the certificate. HttpsClient#checkURLSpoofing method checks server identity according to "RFC 2818: HTTP over TLS" - "Section 3.1 Server Identity".
HttpsClient basically uses HostNameChecker first to check the hostname against the names specified in the certificate. Then, if it fails, HostNameVerifier's turn comes and it's used to verify the host name. As mentioned above, while not overridden, SUN's default behaviour is returning false for this verification. This means, if your HostNameChecker fails, you will get one of the exceptions written in the title according to your URL's hostname type.
So, what can be done to "not-fail" HostNameChecker?
HostNameChecker#match method's implementation is like below;
sun.security.util.HostNameChecker |
public void match ( String hostName, X509Certificate x509certificate ) throws CertificateException { if ( isIpAddress ( hostName )) { matchIP ( hostName, x509certificate ) ; } else { matchDNS ( hostName, x509certificate ) ; } } |
If the incoming hostname is IP, (by matchIP method), it will be searched in available subject alternative names and throw CertificateException("No subject alternative names matching IP address ...") if no matching ip value found.
On the other hand, if the incoming hostname is DNS, (by matchDNS method), it will be searched in available subject alternative names but, different from IP matching algorithm, DNS matching will compare the hostname with the CommonName value from certificate if available. If neither matches with the hostname, a CertificateException("No name matching ... found") will be thrown.
What we can conclude from these details is;
- if you'd like to connect via using IP as hostname;
your certificate should include that ip value as a subject alternative name value (of type IPAddress : key=7).
- if you'd like to connect via using DNS as hostname;
your certificate should either include that DNS name as a subject alternative name value (of type DNS : key=2) or as a CommonName(CN) value.
Hope it helps...