Saturday, January 7, 2012

Changing Tomcat’s CA Trust Keystore File

By default Tomcat makes use of the cacerts file provided with the Java runtime environment located under: C:\Program Files\Java\jre6\lib\security.

Since the JRE installer likes to remove this file when it is updated or uninstalled I needed to find a way to re-configure Tomcat so it uses an different keystore.

System Setup

  1. Start without Java or Tomcat installed.
  2. Install JRE (this is the x64 version): http://download.oracle.com/otn-pub/java/jdk/6u30-b12/jre-6u30-windows-x64.exe
  3. Create JAVA_HOME system wide variable: setx.exe JAVA_HOME "C:\Program Files\Java\jre6" /M
  4. Install Tomcat: http://archive.apache.org/dist/tomcat/tomcat-6/v6.0.29/bin/apache-tomcat-6.0.29.exe
  5. Select all defaults for the Tomcat Normal installation.
  6. Get Portecle to create new and view contents of JKS keystores: http://cdnetworks-us-2.dl.sourceforge.net/project/portecle/portecle/1.7/portecle-1.7.zip

Testing

  1. First we’ll create a test Java webapp that makes an HTTPS connection.
  2. We’ll create a test JSP page (source code at the bottom) using the URLConnection class in Java to connect to https://www.paypal.com/ and fetch its page content.
  3. If the connection is successful the page content is displayed below the “We are connected to HTTPS URL?” message.
  4. If the connection is not successful the exception error message will be displayed.
  5. The test JSP shows we can connect to https://www.paypal.com/ successfully and retrieve it’s content:
image

Why Does This Work?

In order for the URLConnection object to successfully make a connection the certificate from Paypal’s certificate signer must be pre-installed in the JRE cacerts JKS keystore. Let’s make sure…

Paypal’s certificate is signed by a VeriSign certificate.

The SHA1 thumbprint of the VeriSign cert is: 4E:B6:D5:78:49:9B:1C:CF:5F:58:1E:AD:56:BE:3D:9B:67:44:A5:E5

image

We have this VeriSign certificate pre-installed in the JRE cacerts JKS keystore:

image

More Testing…

First lets verify the test JSP works by removing all certificates from the JRE cacerts keystore and re-trying the test webapp.

We’ll use some PowerShell to remove all certificates from the JRE cacerts JKS keystore.

Resultant empty JRE cacerts keystore:

image

For good measure lets restart Tomcat to make sure our changes take effect with a PowerShell one liner: restart-service tomcat*
After restarting Tomcat the HTTPS connection is now broken!

image

As a sanity check we’ll install only the VeriSign certificate in the JRE cacerts keystore all by itself:

image

Now the HTTPS connection works again!

image

Changing the Tomcat Keystore

Because JRE 6 removes it’s cacerts file when it is uninstalled or upgraded I wanted to re-configure Tomcat to use a different keystore so that my certificates being used for HTTPS endpoints wouldn't be deleted. Figuring out how turned out to be quite an adventure.

First I checked the Tomcat documentation for server.xml and found this:

keystoreFile The pathname of the keystore file where you have stored the server certificate to be loaded. By default, the pathname is the file ".keystore" in the operating system home directory of the user that is running Tomcat. If your keystoreType doesn't need a file use "" (empty string) for this parameter.
truststoreFile The trust store file to use to validate client certificates. The default is the value of thejavax.net.ssl.trustStore system property. If neither this attribute nor the default system property is set, no trust store will be configured.

The keystoreFile setting is for Tomcat’s private key for HTTPS connectors and the truststoreFile setting is for client authentication but hey lets try both!

Below is a connector configured to point to a single JKS keystore with the VeriSign certificate inside. Here is the server.xml connector for port 8080.

… And what is inside of C:\mykeystore.jks:

image

After restarting Tomcat things aren't looking good:

image

The solution

It turns out a couple of Java JVM parameters are needed to point Tomcat to a different keystore.

These can be set via the Tomcat configuration utility: "C:\Program Files\Apache Software Foundation\Tomcat 6.0\bin\tomcat6w.exe" //ES//Tomcat6

image

This tool stores values in the registry: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Apache Software Foundation\Procrun 2.0\Tomcat6\Parameters\Java\Options

image

The Tomcat connector was reverted back to the default:

After restarting Tomcat again… The final result: Success! Tomcat is now using C:\mykeystore.jks instead of the JRE cacerts keystore.

image

test.jsp Source Code