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/ .

28 Responses

Subscribe to comments with RSS.

  1. benjaminplee said, on 11.04.09 at 9:34 am

    I forgot to include a large Thank You to fellow Asynchronian Matt Todd for his help figuring all of this out. You can check out his blog over at http://emergentdevelopment.blogspot.com/

  2. Carlos said, on 01.29.10 at 7:20 am

    Hi, thanks for the post!

    I`m working with CLIENT-CERT login but I have troubles with the tomcat-users.xml configuration.

    Please, can you explain how can I map the tomcat`s users and the certificates?? Its the subject?

    Thanks

  3. Selena Rodriguez said, on 02.12.10 at 3:57 pm

    While I agree with the OP regarding the method, I have to argue its application and whether it was actually necessary in this situation…

  4. gerrycharliebillysomeonesusanwillstevewendygerrybradlysomeonesusanstevensteviestevenstanleyrichardryanwilliamsteviestephengerrykimsusan said, on 04.05.10 at 11:02 am

    Excellent post, I like these posts most of all.
    Practical information that everyone can use

  5. zetxek said, on 08.09.10 at 7:54 am

    Any chance you can include the code that you mention? I’m having some trouble making mutual authentication work on tomcat (v 5,6,7, does not mind…). I’d be very grateful!

  6. benjaminplee said, on 08.10.10 at 7:37 am

    zetxek,

    Sure; if I can find it. I will take a look around today and see if I still have a copy of the code.

  7. zetxek said, on 08.10.10 at 7:40 am

    Thank you so much. At the moment I’m having some weird problems with my truststore, I think, with the result of a “ssl_error_handshake_unexpected_alert” error on firefox that does not allow me to display the page. IE simply does not load it…

    • kris said, on 03.26.13 at 2:17 am

      Zetxek, I have got a similar requirement and followed the instructions posted by benjaminplee and got exactly the same error in firefox and IE as posted by you. just wondering if you were able to resolve it and if yes, it would be great if you could share the resolution.

      P.S: benjaminplee, thanks for the wonderful post

      • kris said, on 03.27.13 at 3:17 am

        the error is resolved..posted the resolution in comment below..thanks

  8. Streben said, on 04.04.11 at 3:33 pm

    I have a question about this tuto ,what is the command to create truststore, thks

  9. Doug said, on 05.15.11 at 8:10 am

    “…or from an existing keystore certificate through one hairy keytool command.”

    This command is:
    keytool -importkeystore
    -srckeystore source.p12
    -srcstoretype PKCS12
    -destkeystore destination.jks
    -deststoretype JKS
    -destalias foo

  10. MIdnightJava said, on 05.27.11 at 4:57 pm

    I tried this approach exactly, and it does not work with the . If I leacve that out, I can connect to my web app, with clientAuth = true; so I know my certs and truststore are setup properly. But when I include the with a rolename that matches a rolenam in tomcat-suers.xml, to which is mapped a user whose username is equal to my DN, the browser tells me it could not authenticate with the provided credentials.

    I re-checked all my configurations a dozen times, and I have it all entered correctly. I’m using tomcat 6.0.29.

    Can anyone think of what I might be missing? Is this really working for everyone else?

  11. MIdnightJava said, on 05.27.11 at 5:00 pm

    I had some angle brackets in my post that caused some words to be dropped. Here is my post again without the angle brackets.

    I tried this approach exactly, and it does not work with the auth-constrain . If I leave that out, I can connect to my web app, with clientAuth = true; so I know my certs and truststore are setup properly. But when I include the auth-constraint with a rolename that matches a rolename in tomcat-users.xml, to which is mapped a user whose username is equal to the DN from my cert, the browser tells me it could not authenticate with the provided credentials.

    I re-checked all my configurations a dozen times, and I have it all entered correctly. I’m using tomcat 6.0.29.

    Can anyone think of what I might be missing? Is this really working for everyone else?

  12. MidnightJava said, on 05.27.11 at 9:37 pm

    Turning on SSL debugging (-Djavax.net.debug=ssl) showed me what the problem was. When I saw the cert coming across, I saw that there were spaces between each comma-separated part of the DN. When I copied the precise characters from the debug output to the username attribute in tomcat-users.xml, it worked.

    • User said, on 11.24.15 at 8:10 am

      Where do you place this SSL debug command? That would be helpful for other users as well

      • MidnightJava said, on 11.27.15 at 11:48 pm

        You can turn on SSL debug by setting the value of the java system property “javax.net.debug” to a value of “ssl”. There are various ways to set this property, depending on how you are launching the web server.

        If you’re running the Tomcat web server, you should set the environment variable CATALINA_OPTS accordingly. For example, with Unix bash shell, “export CATALINA_OPTS=-Djavax.net.debug=ssl”. See the comments in catalina.sh (Unix) or catalina.bat (Windows) for an explanation of this and other environment variables used by Tomcat.

        If you’re using a different web server, check the documentation for an explanation of how to set Java system property values.

        If you’re launching a Java app by calling the JVM directly (not the usual or advisable way to start a web server) , you would do something like: “java -Djavax.net.debug=ssl ….”. where the ellipsis is a placeholder for the remainder of the arguments for the java command, e.g. classpath setting, path to main Java .class file, etc.

      • MidnightJava said, on 11.27.15 at 11:53 pm

        Just in case you have other Java options set in your environment when launching Tomcat, you should rather use export CATALINA_OPTS=”$CATALINA_OPTS -Djavax.net.debug=ssl”.

  13. Michel said, on 11.20.12 at 6:04 pm

    I’m having the following problem:
    HTTP Status 401 – Cannot authenticate with the provided credentials

  14. […] on the web on how to configure SSL in Tomcat. Tomcat Server/Client Self-Signed SSL Certificate and Mutual Authentication with CLIENT-CERT, Tomcat 6, and HttpClient stand out. But there no simple example, where we can demonstrate Enabling SSL in Tomcat, I spent […]

  15. Grateful said, on 01.09.13 at 10:08 am

    Thank you very much, I would NEVER thought of putting “CN=TheirApp, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown” as an username…

  16. aldo said, on 01.15.13 at 7:50 am

    Hi quick question, do you know how to add the client certs on runtime, i mean without restarting tomcat

  17. Warner Piñero said, on 01.31.13 at 3:00 am

    benjaminplee, when I tried navigating to the secured/protected page, it didn’t show me a popup dialog to select a certificate. How do we show that popup dialog with the clientAuth set to false in tomcat’s server.xml?

    • kris said, on 03.26.13 at 11:59 pm

      Hi Warner, I am struck up with a similar situation, everything works as expected when clientAuth is set to true or want, but it doesn’t work when it is set to false inspite of setting auth constraint in web.xml. Did it work for you?

      • kris said, on 03.27.13 at 3:16 am

        finally it worked for me. the problem was with jdk version 1.6.0_20 which was giving me this error
        WARNING: SSL server initiated renegotiation is disabled, closing connection

        I upgraded to jdk 1.6.0_35 which has fix for this bug. apparantly the bug has been fixed in jdk 1.6.0_22.

        Apart from this bug, i had done everything as said by benjaminplee.. thanks a ton for this wonderful post.

  18. Mat said, on 05.23.16 at 7:22 am

    I ran into an error running the code in this tutorial. I know this is old, but I’d like to share anyway. I am using an in-memory realm, as stated above, but one thing stuck me as odd. When it says to provide the user cert DN, there is an option for a password. Obviously, a cert will come in without a password, so the tutorial shows to enter “null” for the password parameter. This can cause serious complications. This must be changed to setting the password to “”, rather than “null”, as the literal string “null” will never match the incoming null password.

    Fantastic tutorial otherwise.

  19. kumar said, on 07.13.16 at 5:50 pm

    Hello,
    What if i don’t want to mention a security role in the below steps? Can i still achieve SSL authentication without a role?
    We are trying to do authentication from apigee…

    Configure a new user with the required security role in the tomcat-users.xml if the in-memory realm is used

    Configure out web.xml to use a CLIENT-CERT authentication constraint for our action

    Demo App
    /secure/*

    secureconn

    CLIENT-CERT
    Demo App

    securecon

  20. wittyamit said, on 04.09.18 at 9:44 pm

    I am working on this approach …but with this is it possible to get the browser prompt for the certificate? I am not able to see a browser prompt (FireFox). I am using Tomcat 8.5.28.
    Any help is sincerely appreciated.

  21. wittyamit said, on 04.09.18 at 9:45 pm

    Is someone able to get the browser prompt for certificate with this configuration? I have set up the CLIENT-CERT mutual authentication ..but not able to see a browser (FF) prompt. Any help is sincerely appreciated.


Leave a comment