78

These may be phrased as separate questions for clarity, but they are all related to the same issue.

How are SSL certificate server names resolved?

Why do browsers seem to use the CN field of the certificate, but Java's mechanism seem to only look at "subject alternative names" only?

Is it possible to add alternative names to a SSL certificate using keytool? If not, is using openSSL instead a good option??

Just a little background: I need to get a main server to communicate with several servers using HTTPS. Obviously, we don't want to buy SSL certificates for every server (there could be many), so I want to use self-signed certificates (I have been using keytool to generate them). After I add the certificates as trusted in the OS, the browsers (IE and Chrome) happily accept the connection as trusted. However, even after adding the certificates to Java's cacerts, Java still won't accept the connection as trusted and throws the following Exception:

Caused by: java.security.cert.CertificateException: No subject alternative names present at sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:142) at sun.security.util.HostnameChecker.match(HostnameChecker.java:75) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity(X509T rustManagerImpl.java:264) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted( X509TrustManagerImpl.java:250) at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Clien tHandshaker.java:1185) ... 14 more

I found that I can make Java trust the certificate implementing my own HostNameVerifier, which I copied from here: com.sun.jbi.internal.security.https.DefaultHostnameVerifier just to test (by the way, the hostname passed as an argument to the HostnameVerifier is correct, so I think it should have been accepted).

I have been using the certificate field CN as the hostname (usually the IP address).

Can anybody please tell me if I am doing something wrong and point me in the right direction?

dgvid
  • 24,815
  • 4
  • 37
  • 56
Renato
  • 10,539
  • 3
  • 43
  • 67

1 Answers1

103

How host name verification should be done is defined in RFC 6125, which is quite recent and generalises the practice to all protocols, and replaces RFC 2818, which was specific to HTTPS. (I'm not even sure Java 7 uses RFC 6125, which might be too recent for this.)

From RFC 2818 (Section 3.1):

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.

[...]

In some cases, the URI is specified as an IP address rather than a hostname. In this case, the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI.

Essentially, the specific problem you have comes from the fact that you're using IP addresses in your CN and not a host name. Some browsers might work because not all tools follow this specification strictly, in particular because "most specific" in RFC 2818 isn't clearly defined (see discussions in RFC 6215).

If you're using keytool, as of Java 7, keytool has an option to include a Subject Alternative Name (see the table in the documentation for -ext): you could use -ext san=dns:www.example.com or -ext san=ip:10.0.0.1.

EDIT:

You can request a SAN in OpenSSL by changing openssl.cnf (it will pick the copy in the current directory if you don't want to edit the global configuration, as far as I remember, or you can choose an explicit location using the OPENSSL_CONF environment variable).

Set the following options (find the appropriate sections within brackets first):

[req]
req_extensions = v3_req

[ v3_req ]
subjectAltName=IP:10.0.0.1
# or subjectAltName=DNS:www.example.com

There's also a nice trick to use an environment variable for this (rather in than fixing it in a configuration file) here: http://www.crsr.net/Notes/SSL.html

Bruno
  • 110,518
  • 24
  • 258
  • 357
  • This is exactly what I needed to know... however, it seems Java 6 does not have this -ext option. I will try to change my VM to Java 7 and test this. – Renato Dec 09 '11 at 13:48
  • 2
    Note that you can use keytool from Java 7 on a different machine and copy the keystore across later (you don't have to be running Java 7). Alternatively, I've edited my answer for doing it with OpenSSL. This being said, you might find it more flexible to use host names instead of IP addresses in the long run (using SANs is a good idea anyway). – Bruno Dec 09 '11 at 14:00
  • The -ext option does not work in Java6! I will switch to Java 7 and see if I can do it just using keytool... Thanks very much for the answer.. (PS. will accept the answer once I've tested it) – Renato Dec 09 '11 at 14:03
  • Ok... but I can't use host names because the servers are "dynamic" (they are booted and used on-demand)... Not sure if that would even be possible.... even their IP addresses I believe I will only be able to find during run time. – Renato Dec 09 '11 at 14:06
  • 5
    The fact that you're using IP addresses in your certs is going to be even more a problem in this case. Potentially changing IP addresses is what DNS is for. You have a better chance of solving your problem by using a dynamic DNS service and using certs with that name (or any CNAME that would resolve to that dynamic name). – Bruno Dec 09 '11 at 14:11
  • It seems you're right... I will try to setup host names... hopefully that will be possible even for the "virtual servers" (I have no idea, haven't started looking into them yet, for now just setting up normal servers). – Renato Dec 09 '11 at 14:16
  • Look into services like dyndns.org (and their automated update tools). Make sure the updated addresses are meaningful to the clients that want to connect (depending on the interface and/or if you have reverse NAT). – Bruno Dec 09 '11 at 14:19
  • Ok, I have used keytool to generate certificates with "Subject Alternative Names" and then I was able to establish HttpsURLConnection with other servers. So my problem is solved... However, unless I actually set the CN of my certificate to the IP address of the server, the browsers (IE and Chrome) give me this message: You attempted to reach 111.1.111.11, but instead you actually reached a server identifying itself as . What the hell are the browsers doing here?? Definitely not following RFC2818, Section 3.1! – Renato Dec 12 '11 at 15:09
  • 1
    Just to add some confusion many browsers will accept SAN's like DNS:10.0.0.1 but not IP:10.0.0.1, but the good news is you can have both – KCD May 07 '13 at 01:32
  • Thanks a lot man. This has solved a problem that I was having when trying to make a call from a Jersey client to a server that's not resolvable through DNS (I'm making the call using the server's IP). – Mouhammed Soueidane Oct 24 '13 at 10:58