Hello, I am having some troubles with a self-signed certificate that was given to me to allow my application to use some web services. I installed the certificate in the operating system (Windows) and in the keystore cacerts.jks in glassfish. When I try to consume the webservice I am getting the next error: Error:com.sun.xml.ws.client.ClientTransportException: HTTP transport error: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present I have spent more than 5 days looking for a solution but I do not know what can I do. I read that the problem usually appears when the "cn" in the certificate does not match the address of the server. The "CN" in my certificate is "PRESS44" The server address that was given to me is an ip. https://xxx.xxx.xxx.xxx:123 I have read in different forums that I have to regenerate the certificate and change the cn to make it match with the domain name of the server, in this case to make it match with the ip, but I cannot do it because I cannot change the certificate. I have called to the company who gave the certificate but they told that it was right, and that several people was using the same certificate successfully. I also tried to access the server address from the browser, and it shows me a message telling me that there is a problem with the current certificate. It shows an error that says "mismatched address". http://www.jroller.com/hasant/entry/no_subject_alternative_names_matching "No subject alternative names matching IP address ..." & "No name matching ... found" If you're struggling with the exceptions in the title, read on... 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? If you google for the above exception, most of the results will suggest you to write a custom HostNameVerifier which will always return truelike this;
/** * Host name verifier that does not perform nay checks. */ private static class NullHostnameVerifier implements HostnameVerifier { public boolean verify(String hostname, SSLSession session) { return true; } } |
... and apply this disabled host name checker for current connection only;
/* disabling hostnamechecking only for current connection */ URL url = new URL("https://localhost:9443/soap_rpc"); HttpsURLConnection connection = (HttpsURLConnection)url.openConnection(); connection.setHostnameVerifier(new NullHostNameVerifier()); |
... or for all SSL connections;
/* disabling hostnamechecking for all https connections statically */ .... HttpsURLConnection.setDefaultHostnameVerifier(new NullHostnameVerifier()); |
But, is this the way to go? It didn't satisfied me as most of the skin-deep ways :). So, i decided to go into deep and find a more pleasant solution. Because, regardless of the choosen method among the above ones, you open a gap for URL spoofing at the core of the system. Disabling host name verifier means accepting any hostname in the certificate. BTW, Sun's default HostNameVerifier implementation always returns false since JDK1.4. We didn't get this exception while working on IBM's implementation since it always returns true which means disables host name verification-though their documentation says opposite. 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... Posted by hasanT ( Nov 27 2008, 05:46:43 PM GMT+02:00 ) Permalink
|
Posted by Nenad Popovic on April 03, 2009 at 03:34 PM GMT+02:00 #
After all there's good stuff on the net!
Thanks a lot Hasan!
Posted by Paulo Martins on June 02, 2009 at 12:50 AM GMT+02:00 #
Posted by Srgjan Srepfler on June 09, 2009 at 01:38 PM GMT+02:00 #
You can disable hostnameverifier as mentioned in the post. But this opens a gate for url spoofing.. Although not a good practice, you can disable hostnameverification only for certain ip addresses like this;
/**
* Host name verifier that does not perform for certain ip address */
private static class SpecializedHostnameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
return hostname.equals("xxx.yyy...") ? true : false;
}
}
Posted by Hasan Turksoy on June 09, 2009 at 02:10 PM GMT+02:00 #
We have installed a SSL certificate with IP address in the Subject Alternate field. But the IP address is provided against key type 2. So matchbyIP is failing. When we checked with the CA, they mentioned that they don't provide certificates with IPAddress : key=7 in the SAN field. Is it a standard to use key type 7? Are you aware of any CA that issues certificates in this format?
Any help is appreciated.
thanks,
Posted by Lalitha Bhat on December 29, 2009 at 07:50 PM GMT+02:00 #
Posted by Sergey Karepov on February 07, 2011 at 10:28 AM GMT+02:00 #
Posted by Sumit Kumar on February 28, 2011 at 10:56 AM GMT+02:00 #
Since it's not my certificate, I can't change it. And I don't want to disable checking, unless that's my only option.
have a great day:)
Patrick.
Posted by Patrick Dickey on April 21, 2011 at 02:41 AM GMT+02:00 #
Have a great day:)
Patrick.
Posted by Patrick Dickey on April 21, 2011 at 02:43 AM GMT+02:00 #