CertificateException: No name matching ssl.someUrl.de found


I'm trying to connect to one of my servers through ssl, with Java. I tried a lot of options here is my best try:

I generate a jssecacerts with the recommendet script: http://blogs.oracle.com/andreas/resource/InstallCert.java with the command: java InstallCert ssl.someUrl.de changeit

after this I did the command a second time:

Loading KeyStore jssecacerts...
Opening connection to ssl.someUrl.de:443...
Starting SSL handshake...

No errors, certificate is already trusted

Server sent 1 certificate(s):

 1 Subject [email protected], CN=plesk, OU=Plesk, O=Parallels, L=Hernd
on, ST=Virginia, C=US
   Issuer  [email protected], CN=plesk, OU=Plesk, O=Parallels, L=Hernd
on, ST=Virginia, C=US
   sha1    f1 0d 2c 54 05 e1 32 19 a0 52 5e e1 81 6c a3 a5 83 0d dd 67
   md5     f0 b3 be 5e 5f 6e 90 d1 bc 57 7a b2 81 ce 7d 3d

Enter certificate to add to trusted keystore or 'q' to quit: [1]

I copied the file to the default directory and I loaded the certificate in Java trustStore

System.setProperty("javax.net.ssl.trustStore", "C:\\Program Files (x86)\\Java\\jre6\\lib\\security\\jssecacerts");
System.setProperty("javax.net.ssl.trustStorePassword","changeit");

Then I try to connect

URL url = new URL("https://ssl.someUrl.de/");
URLConnection conn = url.openConnection();
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));

And I get Error on 3rd line: (No name matching ssl.someUrl.de found)

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching ssl.someUrl.de found

Is this cause of the default plesk certificate or is something else wrong?

Setup: JRE 6.20, Netbeans 6.8, Windows7 64bit


Answers:


It looks like the certificate of the server you are trying to connect to doesn't match its hostname.

When an HTTPS client connects to a server, it verifies that the hostname in the certificate matches the hostname of the server. It's not enough for a certificate to be trusted, it has to match the server you want to talk to too. (As an analogy, even if you trust a passport to be legitimate, you still have to check that it's the one for the person you want to talk to, not just any passport you would trust to be legitimate.)

In HTTP, this is done by checking that:

  • the certificate contains a DNS subject alternative name (this is a standard extension) entry matching the hostname;

  • failing that, the last CN of your subject distinguished name (this is the main name if you want) matches the hostname. (See RFC 2818.)

It's hard to tell what the subject alternative name is without having the certificate (although, if you connect with your browser and check its content in more details, you should be able to see it.) The subject distinguished name seems to be:

[email protected], CN=plesk, OU=Plesk, O=Parallels, L=Herndon, ST=Virginia, C=US

(It would thus need to be CN=ssl.someUrl.de instead of CN=plesk, if you don't have a subject alternative name with DNS:ssl.someUrl.de already; my guess is that you don't.)

You may be able to bypass the hostname verification using HttpsURLConnection.setHostnameVerifier(..). It shouldn't be too hard to write a custom HostnameVerifier that bybasses the verification, although I would suggest doing it only when the certificate its the one concerned here specifically. You should be able to get that using the SSLSession argument and its getPeerCertificates() method.

(In addition, you don't need to set the javax.net.ssl.* properties the way you've done it, since you're using the default values anyway.)

Alternatively, if you have control over the server you're connecting to and its certificate, you can create a certificate of it that matches the naming rules above (CN should be sufficient, although subject alternative name is an improvement). If a self-signed certificate is good enough for what you name, make sure its common name (CN) is the host name you're trying to talk to (no the full URL, just the hostname).