How Are Ssl Certificate Server Names Resolved/How to Add Alternative Names Using Keytool

How are SSL certificate server names resolved/Can I add alternative names using keytool?

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

How to add subject alernative name to ssl certs?

Although this question was more specifically about IP addresses in Subject Alt. Names, the commands are similar (using DNS entries for a host name and IP entries for IP addresses).

To quote myself:

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

Note that you only need Java 7's keytool to use this command. Once you've prepared your keystore, it should work with previous versions of Java.

(The rest of this answer also mentions how to do this with OpenSSL, but it doesn't seem to be what you're using.)

Adding an SAN to an SSL cert (in Java)

The JDK7 suggestion is a good one. In the meantime I was able to do it using the Bouncy Castle library. It was educational to do it programmatically instead of with keytool and getting the stores in jks format was straightforward.

How to list unique Subject Alternative Names from a Java TrustStore (JKS) file

As a Java developer a small Java program can do the trick:

public static void main(String[] args) {
String fileName= "website_certs.jks";
char[] password = "".toCharArray();
try {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(fileName), password);
Set<Object> subjAltNames = Collections.list(ks.aliases()).stream().flatMap(alias -> {
try {
return ((X509Certificate) ks.getCertificate(alias)).getSubjectAlternativeNames().stream();
} catch (Exception e) {
return Stream.empty();
}
}).collect(Collectors.toSet());
subjAltNames.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
}
}

The only thing that is strange in your question is that a trust store usually contains root or intermediate CA certificates. But only leaf certificates installed on a web server have a subject alternative name. Therefore this code only works for trust stores that contains leaf/server certificates.

SSL Certificate: How to deal with Common Name and Subject Alternative Name?

As far as I remember, you should put the IP address as one of the Subject Alternative Names (SANs). If the browser can't find the IP Address in the SANs, I don't think it is required to check the CN. You should put the Fully Qualified Domain Name (FQDN) in both the CN and the SANs.

Certificate for <localhost> doesn't match any of the subject alternative names

You need to provide localhost as a subject alternative name when creating your certificate. You can do that by provide the following additional parameter: -ext "SAN:c=DNS:localhost,IP:127.0.0.1"

So something like this:

keytool -genkeypair -keyalg RSA -keysize 2048 -alias stackoverflow -dname "CN=stackoverflow,OU=Hakan,O=Hakan,C=NL" -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -validity 3650 -keystore identity.jks -storepass secret -keypass secret -deststoretype pkcs12

Some explanation:

The SAN field will be used to match the hostname which will be provided in the request. So when you are running your application on localhost, lets say https://localhost:443 and you also want to make a request to that specific host than that hostname should also be available within the SAN field or else it will fail during the handshake process.

Let's grab Stackoverflow as an example. To be able to reach stackoverflow over https we would expect that the certificate should contain at least an entry of stackoverflow.com

Below is the certificate SAN value of stackoverflow with the specific DNS highlighted for this example:
Stackoverflow certificate san values

As you can see already it contains also other dns values. In this way websites owners can use the same certificate for multiple websites/subdomains etc.

How to fix the java.security.cert.CertificateException: No subject alternative names present error?

I fixed the problem by disabling HTTPS checks using the approach presented here:

I put following code into the the ISomeService class:

static {
disableSslVerification();
}

private static void disableSslVerification() {
try
{
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};

// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};

// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}

Since I'm using the https://AAA.BBB.CCC.DDD:9443/ISomeService for testing purposes only, it's a good enough solution, but do not do this in production.

Note that you can also disable SSL for "one connection at a time" ex:

 // don't call disableSslVerification but use its internal code:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
httpsConn.setHostnameVerifier(allHostsValid);
httpsConn.setSSLSocketFactory(sc.getSocketFactory());
}


Related Topics



Leave a reply



Submit