Monday, May 21, 2007

Java SSL client authentication with intermediate CA

Last week I helped a customer with a problem doing client authentication against a web-service. The connection did not work becuse the client software, implemented using JBoss was not sending in its client certificate during the handshake. The client certificate was issued by Verisign, witch uses an intermediate CA to issue the certificates.

The server side was using a Microsoft ISA 2004 server. We started with checking the list of trusted certificate issuers the server was sending out with the help of the openssl toolkit.

openssl s_client -host secure.customer.com -port 443
Look at the list after Acceptable client certificate CA names.
We concluded that the server was only sending a list of self-signed root CA certificates. The server had a lot more CA certificates installed but only a subset was presented as acceptable client authentication CA's.

In Microsoft ISA 2004 you don't have any means of configuring the list of acceptable CA's so we had to look at the other side in the communication.

The client software is implemented using JBoss 4.0.5.GA and JBoss remoting 1.4.3.GA but the as we found out, the problem was a general java problem.

When requested to present a client certificate the Java SSL classes uses an interface called KeyManager to locate the correct credential to use. The default implementation SunX509KeyManagerImpl does a search through all PrivateKeyEntry elements found in the configured keystore. Then doing the search the class will match all certificates in the entry against the list of acceptable CA's.

To get client authentication to work with a client certificate issued by an intermediate CA we need to have the complete chain present in the PrivateKeyEntry in the keystore.

To update a keystore with the correct certificate chain, we can use the openssl toolkit again. First get hold of all certificates in PEM format. To convert a DER encoded certificate to PEM format use:
openssl x509 -in file.der -inform DER -out file.pem
Then create a PKCS#7 encoded certificate chain with:
openssl crl2pkcs7 -nocrl -out chain.pem -certfile root.pem -certfile intermediate.pem -certfile clientcert.pem
How this complete certificate chain can be imported into the java keystore using the standard keytool program. But first we need to know the alias of the key we are going to update. We can list the content of the keystore with:
keytool -list -keystore keystore-file
During the import of the certificate chain, keytool will check that the certificates match the private key, so it's should not be possible to corrupt the keystore. But as always backup the file if your production depends on it...

To import the chain, use:
keytool -import -keystore keystore-file -alias alias -file chain.pem
Keytool will ask if you trust the certificate issuer, just answer Yes.

We can now check the content of the keystore again and make sure that the complete chain is present with:
keytool -list -v -keystore keystore-file
This command will do a verbose listing of the keystore, with all certificates.