How to Fix the exception ” java.security.cert.CertificateException: No name matching localhost found”, when client uses URL with “localhost” as host name to access some secure service over HTTPS? In this article we will check the root cause of the issue and will try two different solutions two fix the same.
There are other similar issues related the SSL certificates. One of the common situation is the missing certificate in trust store. In that case you may see the following error message.
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
See the following article to read more about the above error and its solution.
PKIX Path Building Failed (Validation) : sun.security.validator.ValidatorException
Table of Contents
- When Do You Encounter the Error : java.security.cert.CertificateException: No name matching localhost found?
- Root Cause of the Exception: java.security.cert.CertificateException: No name matching <host> found
- How to solve the error “java.security.cert.CertificateException: No name matching localhost found”?
- References
When Do You Encounter the Error : java.security.cert.CertificateException: No name matching localhost found?
When you use self signed certificate for services such as secure web service and try to access the service over HTTPS from a client using “localhost”, you may encounter the error “java.security.cert.CertificateException: No name matching localhost found”.
The error can also be something like “java.security.cert.CertificateException: No name matching <hostname> found”, where, the “hostname” can be anything.
….. 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) …….
Root Cause of the Exception: java.security.cert.CertificateException: No name matching <host> found
At run-time, Java check host name against the names specified in a digital certificate as required for TLS and LDAP.
So if you are using for example, https://localhost:8443/MyService/.. , the runtime will check if the certificate allows use of the given DNS name, in this case “localhost”. This matching is performed using the matching rules specified by RFC2459.
As per the java doc, “If a subjectAltName extension of type dNSName is present, that MUST be used as the identity. Otherwise, the (most specific) Common Name field in the Subject field of the certificate MUST be used. Although the use of the Common Name is existing practice, it is deprecated and Certification Authorities are encouraged to use the dNSName instead.”
Also “If more than one identity of a given type is present in the certificate (e.g., more than one dNSName name, a match in any one of the set is considered acceptable.)”
The above points indicate that, the certificate CN (Common Name) should be the same as host name in the URL. If the CN in the certificate is not the same as the host name, you will get the error “java.security.cert.CertificateException: No name matching <the_host_name_used> found”, where , “the_host_name_used” is the host name you used as part of the URL in your client application.
How to solve the error “java.security.cert.CertificateException: No name matching localhost found”?
Method 1: Change the Certificate CN name
The first step is to verify the CN (Common Name) in the certificate. Please not that, you cannot change the CN in an already created certificate. This is because, a certificate is designed specifically so that this data can’t be modified after its creation. It is part of the certificate.
CN=localhost, OU=home, O=home, L=city,ST=state, C=in
So you need to create a new certificate with the hostname as “localhost” or any other hostname you want to use. Example is given below. You can use the java keytool tool to create your self-signed certificate with the “localhost” CN.
F:\myservice\src>keytool -genkey -alias serverkey -keypass nosecret -keyalg RSA -sigalg SHA1withRSA -keystore server.keystore -storepass nostoresecret What is your first and last name? [Unknown]: localhost What is the name of your organizational unit? [Unknown]: globinch ws What is the name of your organization? [Unknown]: globinch What is the name of your City or Locality? [Unknown]: Bangalore What is the name of your State or Province? [Unknown]: KAR What is the two-letter country code for this unit? [Unknown]: IN Is CN=localhost, OU=globinch ws, O=globinch, L=Bangalore, ST=KAR, C=IN correct? [no]: yes
That’s all. You need to apply this certificate and setup the web server. Now if you use endpoint such as https://localhost:8443/MyService/..” ,the java security run-time will accept it.
Implement the verify() method of HostnameVerifier
HostnameVerifier is the base interface for hostname verification. As per java doc “During handshaking, if the URL’s hostname and the server’s identification hostname mismatch, the verification mechanism can call back to implementers of this interface to determine if this connection should be allowed”.
These callbacks are used when the default rules for URL hostname verification fail.
The HttpsURLConnection class uses HostnameVerifier and SSLSocketFactory. You can set the HostnameVerifier using the setDefaultHostnameVerifier(setDefaultHostnameVerifier) method. See example below.
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; } });
So during handshaking, if the URL’s hostname and the server’s identification hostname mismatch, it will invoke the above verify implementation. The above code returns “true” for hostname “localhost”, and the connection is allowed.
References:
Incoming search terms:
- HowtoFix:java security cert CertificateException:Nonamematchinglocalhostfound
- java security cert CertificateException
- java security cert certificateexception: no name matching localhost found
- java security cert CertificateException: No subject alternative names presen
- java security cert CertificateException: No subject alternative names present
- https://java globinch com/enterprise-java/security/fix-java-security-certificate-exception-no-matching-localhost-found/
- ccilweb
- CertificateException
Helped a lottt.. Thank you.. 🙂
NIce article..I have question about Method 1: Change the Certificate CN name:When i am using any external name other than localhost..e.g xyz.company.com then it is not working
cold you please tell me what is another reason
First method/advice worked for me
Well explained, but the first fix does not work. I see the same error message as when the CN did not match.
No connection
lost certification of javax
I have solved the issue by the following way.
1. Creating a class . The class has some empty implementations
class MyTrustManager implements X509TrustManager {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString)
throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString)
throws CertificateException {
// TODO Auto-generated method stub
}
2. Creating a method
private static void disableSSL() {
try {
TrustManager[] trustAllCerts = new TrustManager[] { new MyTrustManager() };
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance(“SSL”);
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
3. Call the disableSSL() method where the exception is thrown. It worked fine.
Aditya Bhuyanp’s method could work!