How to avoid java.security.cert.CertificateException: No name matching localhost found

When you create a self signed certificate to enable HTTPS on your local server for test, and then a java client (it can be the webapp itself) tries to access your localhost, it might face the following exception:

Caused by: 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:1611)
	at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:187)
	at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:181)
	......
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)
	......

This happens because, quoting from lots of places out there:

Java by default verifies that the certificate CN (Common Name) is the same as host name in the URL. If the CN in the certificate is not the same as the host name, your web service client fails.

So, a valid solution would be create an implementation of HostnameVerifier that returns ok for localhost, regardless of the certificate. Something like:

javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
    new javax.net.ssl.HostnameVerifier(){

        public boolean verify(String hostname,
                javax.net.ssl.SSLSession sslSession) {
            if (hostname.equals("localhost")) {
                return true;
            }
            return false;
        }
    });

The problem of this approach is that you have to add new code to the client, be careful to remove it for production, and if your client is another webapp, it will probably mean another deploy (it gets worse when you don’t own the code of the app.

An easier solution to this problem would be simply create your self-signed certificate with the “localhost” CN.

So, lets say we are following this article for setting up SSL on Tomcat. Then, instead of:

keytool -genkey -alias tomcat -keyalg RSA
Enter keystore password:  password
Re-enter new password: password
What is your first and last name?
  [Unknown]:  Your Own Name
What is the name of your organizational unit?
  [Unknown]:  home
What is the name of your organization?
  [Unknown]:  home
What is the name of your City or Locality?
  [Unknown]:  City
What is the name of your State or Province?
  [Unknown]:  State
What is the two-letter country code for this unit?
  [Unknown]:  US
Is CN=Your Own Name, OU=home, O=home, L=City, ST=State, C=US correct?

Change the CN (your name) to localhost

keytool -genkey -alias tomcat -keyalg RSA
Enter keystore password:  password
Re-enter new password: password
What is your first and last name?
  [Unknown]:  localhost
What is the name of your organizational unit?
  [Unknown]:  home
What is the name of your organization?
  [Unknown]:  home
What is the name of your City or Locality?
  [Unknown]:  City
What is the name of your State or Province?
  [Unknown]:  State
What is the two-letter country code for this unit?
  [Unknown]:  US
Is CN=localhost, OU=home, O=home, L=City, ST=State, C=US correct?

Done. Set your webserver up to use the new certificate and the clients will accept it.

in Howtos | tags , , , , ,

2 Responses to How to avoid java.security.cert.CertificateException: No name matching localhost found

  1. Pingback: Apache Tomcat and SSL | martin steffen

Leave a Reply