Two Guys Arguing

Mutual Authentication with CLIENT-CERT, Tomcat 6, and HttpClient

Posted in java by benjaminplee on 11.03.09

M is for Mutual Authentication:

How “it must be simple” turned into “god that was annoying”.

I spent most of today wrestling with getting a JEE webapp running in Tomcat 6 to enforce mutual authentication by using the rarely used CLIENT-CERT authentication.  If you love spending your days translating poor error messages and frustrations into Google queries as much as I do, read on for some notes I took along the way.  If not, check out this amazing chart over at XKCD.

The mission: lock down a particular action on our web application so that only a single other system could authenticate and invoke it.  Also provide an example client application which can access the webapp.

Back story: I have worked with certificates and various forms of authentication before, but never this combination.  We want a small subset of the application to only be accessed via SSL with both server and client providing trusted certificates for mutual authentication.  The portion of the application will only be accessed by a single other system inside the company intranet.

How it went down:  The following are the basic steps to get everything setup in Tomcat 6 and a fairly standard JEE webapp.

  • First we need to know who we are, who they are, and make sure we ONLY trust them and no one else
    • To do this we need a certificate to identify ourselves.  Java’s provided keytool.exe provides an easy way to create a self signed certificate within a keystore.  Keytool can also help us export our public certificate out of our keystore.  This should also be done for the client.
    • Next import the public certificates into a new truststore for the opposite system (A only trusts B and B only trusts A).  Keytool to the rescue again.
    • Note: Java keystore files and certs of in a different format than the PKCS12 files that can be created by OpenSSL
  • Next configure a Tomcat to support SSL communication
    • Add a new connector to thees server.xml configuration specifying HTTPS, the SSL protocol, where the webapp’s keystore and truststores are, etc
    • <Connector className=”org.apache.coyote.tomcat4.CoyoteConnector”
      port=”8443″ enableLookups=”true”
      acceptCount=”100″ connectionTimeout=”20000″
      useURIValidationHack=”false” disableUploadTimeout=”true”
      scheme=”https” secure=”true” SSLEnabled=”true”
      keystoreFile=”ourApp.keystore” keystorePass=”changeit”
      truststoreFile=”ourApp.truststore” truststorePass=”changeit”
      clientAuth=”false” sslProtocol=”TLS”
      />
    • Note: clientAuth can have 3 values
      • True – all connections through this connector require client authentication
      • Want – the web app will ask for authentication but not require it
      • False – connections do not require client authentication UNLESS the web app specifies it is required via a security constraint with CLIENT-CERT chosen (this is the one we want)
  • Configure a new user with the required security role in the tomcat-users.xml if the in-memory realm is used
    • <role rolename=”secureconn”/>
    • <user username=”CN=TheirApp, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown” password=”null” roles=”secureconn”/>
  • Configure out web.xml to use a CLIENT-CERT authentication constraint for our action
    • <security-constraint>
      <web-resource-collection>
      <web-resource-name>Demo App</web-resource-name>
      <url-pattern>/secure/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
      <role-name>secureconn</role-name>
      </auth-constraint>
      </security-constraint><login-config>
      <auth-method>CLIENT-CERT</auth-method>
      <realm-name>Demo App</realm-name>
      </login-config>

      <security-role>
      <role-name>secureconn</role-name>
      </security-role>

  • Create a client to hit the application
    • Here we used the Apache Commons HttpClient (3.x b/c of client restrictions)
    • Several pages talk about various ways to add security protocols to HttpClient code but the examples are left lacking and most point to the AuthSSLProtocolSocketFactory class which for some reason is not present int he 3.x binaries but IS present in the 3.x SRC bundles.
    • ** I will include some sample code tomorrow ….
  • Test things using Firefo
    • You can add a personal certificate to identify yourself through Firefox’s preferences -> Advanced -> Exncryption
    • Firefox uses PKCS12 certificates which can be created easily from scratch through a few OpenSSL commands or from an existing keystore certificate through one hairy keytool command
    • ** I will include the commands and a couple links tomorrow …..

Hopefully these notes will help someone in the future with getting things setup correctly.  When I started this morning I couldn’t find an end to end example to build on.

Edit: Huge thanks to Matt Todd for helping me with this.  You can check out his blog at: http://emergentdevelopment.blogspot.com/ .