12

I am trying to make a Tomcat web application use client certificate authentication for incoming connections. Everything works fine when using clientAuth=true in server.xml, however due to other applications running on the same server, we cannot use this in the production environment.

Is there a way to form a web.xml document such that it forces client certificate usage for the application in the same way as clientAuth=true? It seems like using the CLIENT-CERT setting also requires you to setup a tomcat user account for each certificate which is to access your system? We need to be able to allow all certificates which are from a specified CA (set in the server truststore) where the subject matches certain rules (checked within the actual application). I was hoping that something like the following would work, but no luck yet!

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Everything</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>
<login-config>
    <auth-method>CLIENT-CERT</auth-method>
</login-config>
user unknown
  • 421
  • 1
  • 5
  • 14

2 Answers2

7

First of all, it sounds like you want clientAuth=want instead of clientAuth=true: that will allow the client to provide a certificate but not absolutely require one.

When you use authentication of any kind, Tomcat (or any servlet container for that matter) must be able to build a Principal object out of it -- one that has a name (usually a username). The container then must decide what roles the user has in order to properly authorize a particular request. So, Tomcat will need to know about the users beforehand in order to make authorization work.

On the other hand, if you don't need any authorization, you could set clientAuth=want and then use a Filter to verify the certificate. There's no need to use CLIENT-CERT authentication if you are already doing your own checking.

Christopher Schultz
  • 20,221
  • 9
  • 60
  • 77
  • 1
    `clientAuth=false` in the connector config and `CLIENT-CERT` in `web.xml` allows you to get the client-certificate for re-negotiation, which is handy if you don't want/need other webapps to cause a prompt in the browser. – Bruno Aug 01 '12 at 18:58
  • 1
    That's what I was trying to do above, but it looks like I may have misunderstood the usage of the CLIENT-CERT option here. So, CLIENT-CERT in the web.xml is **only** for using a cert to authenticate as a particular user? Most of my experience so far is with IIS, and I was hoping Tomcat worked the same way, where you can specify whether the entire server, or an individual project or even an individual page needs a valid client certificate to be presented before a connection is allowed. – user unknown Aug 02 '12 at 08:39
  • @Christopher-Schultz: I had tried clientAuth=want, which did work but I wasn't sure if it was as secure. However, thinking about it some more - if no certificate is presented, our code will work that out anyway and reject them. If they do present a client certificate, it will presumably have to go through all the checking that Tomcat would do when clientAuth=true (e.g. trusted CA, validity in range, etc.) before being passed to our code for further validation so it will work exactly the same? – user unknown Aug 02 '12 at 08:45
  • 1
    If your webapp requires `CLIENT-CERT` authentication, then the webapp itself will demand a certificate to access protected resources. The `clientAuth=want` just prevents the `` from demanding a certificate for *all* requests. If you enable Tomcat's certificate validation, the validation will be preformed if the certificate is present, but skipped if it is not present. I think you'll be fine with the configuration I have presented. – Christopher Schultz Aug 02 '12 at 14:45
  • 1
    I forgot to mention: for trusting all certificates signed by a CA, you just have to configure your truststore properly. – Christopher Schultz Mar 13 '13 at 14:38
5

I was just reseaching solution to above problem and finally found a solution:

  1. Configure tomcat with connector clientAuth="false" attribute(Otherwise all secure connections to server will do mutual, client server, ssl authentification.

  2. Add the following in web.xml(I have just shown here example)

    <security-constraint>
        <web-resource-collection>
            <url-pattern>/LoginTestServlet1</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>manager</role-name>
        </auth-constraint>
        <user-data-constraint>
            <!-- transport-guarantee can be CONFIDENTIAL, INTEGRAL, or NONE -->
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
     <security-constraint>
        <web-resource-collection>
            <url-pattern>/LoginTestServlet2</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>manager</role-name>
        </auth-constraint>
       <!--  <user-data-constraint>
            transport-guarantee can be CONFIDENTIAL, INTEGRAL, or NONE
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>  
        </user-data-constraint> -->
    </security-constraint>
     <login-config>
        <auth-method>CLIENT-CERT</auth-method>
        <realm-name>certificate</realm-name>
      </login-config>
    

    manager

  3. In tomcat users-users.xml add the following(pls note that if trust store has almost identical certificates then you should clearly identify your certificate as follows)

    <role rolename="manager"/>
    

    < user username="EMAILADDRESS=hamzas100@yahoo.com, CN=KS, OU=OFF, O=OFS, L=Bukhara,
    ST=Bukhara, C=UZ" password="" roles="manager"/>

  4. put in a browser(or curl) address line:

    https://yourdomain.com:8443/LoginTest/LoginTestServlet1 or
    https://yourdomain.com:8443/LoginTest/LoginTestServlet2

  5. For this to work you have to add certificate to browser personal certificate list(if you are testing with browser). I have tried with Mozilla Firefox and it will easyly let you do this.(But it only accepts b12 certificate so it is suggested that you should use openssl with java keytool). If everything configured right then you will get prompt from mozilla to choose certificate from existing ones. If you are using curl(it is used for automatic web interfaces testing then use the following commant line to test(I have just given an example here.) Plese note that you should choose certificate which you imported into trust store.

    curl -s -k --cert selfsigned.pem --key key.pem -v --anyauth https://yourdomain.com:8443/LoginTest/LoginTestServlet1 --cacert selfsigned.pem or curl -s -k --cert selfsigned.pem --key key.pem -v --anyauth http://yourdomain.com:8080/LoginTest/LoginTestServlet2 --cacert selfsigned.pem

Note: My connector looks like as follows:

<Connector port="8443"
       maxThreads="150"  scheme="https" secure="true" SSLEnabled="true"
       sslProtocol="TLS" keystoreType="PKCS12"  truststoreType="PKCS12" clientAuth="false"
               keystoreFile="C:/Program Files/glassfish-3.1.2/glassfish/domains/domain1/config/cacerts.pkcs12"
               truststoreFile= "C:/Program Files/glassfish-3.1.2/glassfish/domains/domain1/config/cacerts.pkcs12"
               truststorePass="changeit"
               keystorePass="changeit"
               protocol="org.apache.coyote.http11.Http11Protocol">
  • I have tested this on tomcat 6.0.35 but it sould work with glassfish too with some changes to glassfish-web.xml file – Khurshed Salimov Jun 16 '13 at 08:18
  • manager EMAILADDRESS=hamzas100@yahoo.com, CN=KS,OU=OFF,O=OFS,L=Bukhara,S=Bukhara,C=UZ manager – Khurshed Salimov Jun 16 '13 at 08:20
  • some certificates are easy to add to browser personal store like pkcs12 certificates. For them to add to personal certificate store of a Crome/Cromium and Internet Explorer browsers(new versions) just double click on them and follow steps in wizard. For others like Mozilla Firefox v 21 you have to do it manualy on browser. – Khurshed Salimov Jun 16 '13 at 11:22
  • THe case with Opera is the same as Mozilla in terms of adding certificate for client certificate authentification – Khurshed Salimov Jun 16 '13 at 11:23
  • All browsers support multiple certificates – Khurshed Salimov Jun 16 '13 at 15:54
  • Actually I tries on glassfish 3.1.2 and it works under following conditions: – Khurshed Salimov Jun 16 '13 at 15:55
  • It also worked under Glassfish 3.1.2 version too. But I took a diffrent approach on configuration which I explained here: http://stackoverflow.com/questions/10254610/glassfish-3-1-2-configuration-client-certificate-for-mutual-authentication – Khurshed Salimov Jun 16 '13 at 16:30
  • I you can see in answer that tomcat lets you change trust and key store location and type. – Khurshed Salimov Jun 16 '13 at 16:53
  • Question, what if I wanted the client to be another tomcat server? Do you think this would be possible? In my setup, I have top level tomcat, that sends out serialized object requests via port 443 requests to other tomcats around the country. All servers are protected behind the same firewall(s) but I'd like to add more security than leaving them wide open. I'd like to have certificate based authentication. Any advice would be great! Thank you. – D-Klotz Sep 27 '13 at 21:06