• Configuring Tomcat SSL Client/Server Authentication

    Setting up Tomcat to provide self-signed SSL certificates allowing secure client/server communication is well-documented and relatively easy to set up. It’s almost as easy to set up a configuration where Tomcat requires the client to specify an SSL certificate as well, but sadly nowhere near as well-documented since this is a much less-common use case.

    It’s exactly this use case that we needed to put in to place with a secure internal system that we’re working on. To make it as hack-proof as possible, we’re requiring that both the client and the server know about each other and authenticate each other before beginning any communication. I found all the individual fragments of what I needed to get this set up online, but no single resource covered the entire process in detail.

    Since I spent the better part of the day today figuring out how to get this to work, I figured I would share the complete configuration with the world, including Tomcat config, keytool commands, and how to connect a secured client/server from both Java and Safari on Mac OS X. Hopefully this will save the next guy some time.

    Overview

    With traditional SSL, a server presents a certificate to a user agent (such as a web browser) to both

    (a) allow the user agent to validate the server’s identity, and
    (b) begin the process of establishing a secure connection handshake.

    In this configuration, the user agent is not required to authenticate itself, since it is only the server that may be suspect – you don’t want to send your credit card details to a rogue server, but any server (rogue or legitimate) will happily accept credit cards or other sensitive data from any client willing to submit them, so there’s no need to validate the clients as far as the server is concerned.

    In higher-security configurations, the server also requires the client to identify itself by providing its own certificate. Examples include cases where the server has access to sensitive data/capabilities that it doesn’t want to allow any client to access. To facilitate this, the server maintains a keystore containing copies of certificates issued to trusted clients to whom the server will allow access. If a client is unable to supply a certificate, or the certificate that they supply is not in the server’s keystore of trusted certificates, access to the server’s resources is denied.

    The Setup

    The server is configured so that it will present a certificate to clients when an SSL connection is requested. This is achieved by setting up a keystore and providing the location and credentials for the keystore to Tomcat.

    As part of this configuration, Tomcat is told to require clients to present their own certificates authenticating them for access to the server. This is achieved by generating client certificates, and installing copies of these certificates in to the same server keystore used for presenting server SSL (described in the previous paragraph).

    When a client connects to the server, it needs to present a certificate when challenged to do so. To find the certificate that it will provide to the server, the client references its own keystore containing a copy of its certificate. As described in the previous paragraph, a copy of the same client certificate is installed in the server’s keystore (the one with the list of trusted client certificates).

    Finally, if the server’s certificate is self-signed and could be considered untrustworthy, then the client’s keystore must contain a copy of the server’s certificate so that it knows that the server is trustworthy. To facilitate this, a copy of the server’s public SSL certificate is installed in the client keystore as well.

    Keystore and Certificate Generation With keytool

    Java provides a handy command-line tool called keytool that you can use to generate keystores. Each keystore has a private key within it which cannot be exported, so in order to allow the client to share a certificate with the server, keytool lets you export a public version of the private key as a certificate. Exactly why and how this works is beyond the scope of this blog post, so check out some info on how SSL works elsewhere.

    By following the instructions below, you will create two keystores: client.jks (for the client to use) and server.jks (for the server to use). In order to provide copies of the client’s certificate to the server (and vice versa), you will export public certificates based on the private keys. Finally, you will install the server’s public certificate in to the client’s keystore and vice versa, allowing both the client and server to properly authenticate and trust each other when a secure connection is established.

    Generate the Client and Server Keystores

    keytool -genkeypair -alias serverkey -keyalg RSA -dname "CN=Web Server,OU=Application Development,O=Highwinds,L=Winter Park,S=FL,C=US" -keypass password -keystore server.jks -storepass password
    keytool -genkeypair -alias clientkey -keyalg RSA -dname "CN=client,OU=Application Development,O=Highwinds,L=Winter Park,S=FL,C=US" -keypass password -storepass password -keystore client.jks

    Export the Client’s Public Certificate and Import it in to the Server’s Keystore

    keytool -exportcert -alias clientkey -file client-public.cer -keystore client.jks -storepass password
    keytool -importcert -keystore server.jks -alias clientcert -file client-public.cer -storepass password -noprompt
     
    # view the contents of the keystore (use -v for verbose output)
    keytool -list -keystore server.jks -storepass password

    Export the Server’s Public Certificate and Import it in to the Client’s Keystore

    keytool -exportcert -alias serverkey -file server-public.cer -keystore server.jks -storepass password
    keytool -importcert -keystore client.jks -alias servercert -file server-public.cer -storepass password -noprompt
     
    # view the contents of the keystore (use -v for verbose output)
    keytool -list -keystore client.jks -storepass password

    Configure Tomcat for SSL Using the Server Keystore
    Make the following addition to {tomcat.home}/conf/server.xml, substituting the entries for keystoreFile, keystorePass, truststoreFile, and truststorePass with the appropriate paths/passwords from your implementation. These paths will point to the keystores created earlier in this process.

    Note the clientAuth attribute – this is the attribute that causes Tomcat to require the client to provide a certificate; with this option, if no certificate is provided by the client then the connection is terminated by Tomcat immediately. If this is set to false, no client certificate is required or validated (useful for testing server-only SSL configuration). If clientAuth is set to want, client certificates are validated if presented to the server, but are not required.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    <Connector
       clientAuth="true" port="8443" minSpareThreads="5" maxSpareThreads="75"
       enableLookups="true" disableUploadTimeout="true"
       acceptCount="100" maxThreads="200"
       scheme="https" secure="true" SSLEnabled="true"
       keystoreFile="/Users/mporges/Desktop/tomcat-ssl/final/server.jks"
       keystoreType="JKS" keystorePass="password"
       truststoreFile="/Users/mporges/Desktop/tomcat-ssl/final/server.jks"
       truststoreType="JKS" truststorePass="password"
       SSLVerifyClient="require" SSLEngine="on" SSLVerifyDepth="2" sslProtocol="TLS"
    />

    You must restart Tomcat in order for this configuration change to take effect. Note that you must also restart Tomcat whenever you change the contents of the keystore; they are cached at launch and are not re-examined until the server process is bounced.

    Sample Client Application in Java

    The client application presented below uses Apache’s Commons HTTPClient library to establish a secure connection to the Tomcat server. Note the system properties defined in the static initializer; these properties tell the JVM where to find the trust store (telling it which self-signed server certificates it should trust) and the keystore (telling it where to find its own client certificates, which it will then present to the server when challenged). We happened to store both these items in the same keystore, but if you really wanted to you could split them up in to separate files.

    import java.io.IOException;
     
    import org.apache.commons.httpclient.HttpClient;
    import org.apache.commons.httpclient.HttpException;
    import org.apache.commons.httpclient.URI;
    import org.apache.commons.httpclient.methods.GetMethod;
     
     
    public class ClientConnectionTest
    {
       static
       {
          System.setProperty("javax.net.ssl.trustStore", "/Users/mporges/Desktop/tomcat-ssl/final/client.jks");
          System.setProperty("javax.net.ssl.trustStorePassword", "password");
          System.setProperty("javax.net.ssl.keyStore", "/Users/mporges/Desktop/tomcat-ssl/final/client.jks");
          System.setProperty("javax.net.ssl.keyStorePassword", "password");
       }
     
       /**
        * @param args
        * @throws IOException 
        * @throws HttpException 
        */
       public static void main(String[] args) throws HttpException, IOException
       {
          HttpClient client = new HttpClient();
          GetMethod method = new GetMethod();
          method.setURI(new URI("https://localhost:8443", false));
          client.executeMethod(method);
     
          System.out.println(method.getResponseBodyAsString());
       }
    }

    Configuring a Client Certificate in Safari on Mac OS X

    Typically, in a secure client/server setup such as the one we are creating, the client and server will both be application servers. However, nothing stops you from setting up your browser to be a secure client with its own certificate. This is an ultra-secure way to set up restricted access to a web site so that only certain known users can access the web site.

    Unfortunately, the creation and installation of the client certificates and the setup process for letting the browser know to send the certificate to the server are both operating system- and browser-dependent processes. The only one I have had success trying is Safari on Mac OS X, so I have included those instructions here. For instructions on setting up another browser (such as Firefox or Internet Explorer) on other operating systems, Google is your friend.

    How To Create and Install a Client Certificate in Safari on Mac OS X
    In order to connect to the secure server we created earlier from Safari in Mac OS X, a certificate must be created in the Keychain Access application and installed in the server’s keystore before connections will be accepted. This is achieved as follows.

    1. Open Keychain Access.
    2. In the menu bar, go to Keychain Access => Certificate Assistant => Create a Certificate…, which launches an assistant.
    3. Provide a name for the certificate (your own name is appropriate). Select “Self-Signed Root” for “Identity Type” and “SSL Client” for “Certificate Type.” At your option, you may select the “Let me override defaults” setting to specify items such as the key pair key size/algorithm (2048/RSA by default), Key Usage Extensions (only “Signature” by default), Extended Key Usage Extensions (only “SSL Client Authentication” by default), Basic Constraints Extension, Subject Alternate Name Extension, and keychain Location for Certificate (login by default).
    4. After completing the assistant, you will find a new certificate under My Certificates in Keychain Access (under Category in the lower left hand side of the window).
    5. To export the certificate, right click it and select “Export {certificate name}…” Choose a suitable location to save the certificate as a .cer file.
    6. In the command line, navigate to the location of the server.jks keystore created earlier, and enter the following command:
      keytool -importcert -keystore server.jks -alias safari-osx-cert -file {exported certificate file name}.cer -storepass password -noprompt
    7. Restart Tomcat if it is running so that it will pick up the new client certificate that you just installed.
    8. Open Safari and navigate to the location of the Tomcat server. If Tomcat is using a self-signed certificate, you will be prompted with a warning and will have to establish trust for the certificate (either permanently by checking the appropriate checkbox, or just for the life of the browser process by selecting “Continue”).
    9. You will then be challenged for a client certificate; accept the challenge. Safari should select the certificate you created, or alternatively it will prompt you to select a certificate; if so, select the certificate you created in this process. Once a certificate is selected you should not be challenged again for the life of the browser process.
    10. If Safari is unable to select the appropriate certificate automatically, you may need to set up an Identity Preference in Keychain Access. To do so, follow the instructions on Apple’s knowledge base.

    Category: Uncategorized | Tags: