YOU’RE NOW READING

Mutual Authentication in Tomcat using Self-Signed Certificates

calendar_today Posted 9 months ago · 10 minute-read · Technology

Due to an appli­ca­tion I’m devel­op­ing at work, I’ve had to try out client authen­ti­ca­tion in a Tom­cat 8 envi­ron­ment. What this does is it enables mutu­al authen­ti­ca­tion between the client and the serv­er.

This is most­ly used in enter­prise sys­tems where users must iden­ti­fy them­selves by using cus­tom CA-signed cer­tifi­cates, for exam­ple in bank­ing or pub­lic admin­is­tra­tion.

When mutu­al authen­ti­ca­tion is enabled and prop­er­ly con­fig­ured on a web serv­er, users vis­it­ing the web­site are greet­ed with a prompt that asks them choose a cer­tifi­cate to present as iden­ti­fi­ca­tion:

Exam­ple prompt using my self-signed cer­tifi­cate

Let’s check out all the required steps need­ed in order to con­fig­ure this tech­nol­o­gy and enable it in a Tom­cat 8 envi­ron­ment run­ning on Win­dows.

Table of Contents

  1. Get­ting Start­ed
  2. Cre­at­ing a Self-Signed Cer­tifi­cate Author­i­ty
  3. Cre­at­ing a Serv­er Cer­tifi­cate for our Tom­cat Serv­er
  4. Enabling TLS in Tom­cat with a Self-Signed Cer­tifi­cate
  5. Cre­at­ing a Client Cer­tifi­cate for Mutu­al Authen­ti­ca­tion
  6. Sup­port­ing Addi­tion­al Cer­tifi­cate Author­i­ties
  7. Wrap­ping Up!

0.
Getting Started

  1. Install the required OpenSSL bina­ries from here: https://slproweb.com/products/Win32OpenSSL.html.
    You’ll want the 64-bit ver­sion.

  2. Add the bin fold­er to your path envi­ron­ment vari­able for con­ve­nience. The path to add should be this one:
    C:\Program Files\OpenSSL-Win64\bin

  3. Also add your JDK bin fold­er to your path, again, for con­ve­nience:
    C:\Program Files (x86)\Java\jdk1.8.0_192\bin
    (change to your JDK loca­tion)

  4. I cre­at­ed a fold­er named “keys” in my root Tom­cat fold­er
    C:\apache-tomcat-8.5.51\keys to han­dle all the cer­tifi­cate stuff, you can do this too. Just make sure you have a place ded­i­cat­ed to this since we’ll be gen­er­at­ing quite a few files.

  5. Run cmd as an admin­is­tra­tor and cd to the “keys” fold­er in Tom­cat.

I.
Creating a Root Certificate Authority

Root Cer­tifi­cate Author­i­ties (CA from now on) are used to gen­er­ate new cer­tifi­cates that are con­fig­ured on the serv­er in order to allow secure encrypt­ed traf­fic. If we head to google.com and check out the cer­tifi­cate they’re serv­ing us, we can see their root CA:

Google’s Root CA “GTS CA 1O1”

In Google’s case, they have a CA named GTS CA 1O1 which signed their cer­tifi­cate for use on *.google.com domains.

CAs root cer­tifi­cates come bun­dled with soft­ware. For exam­ple, browsers like Google Chrome or Mozil­la Fire­fox come with a set of trust­ed root CAs.

With soft­ware updates, root CAs are added and removed from the trust­ed cer­tifi­cate store, so it’s impor­tant that you keep your browsers updat­ed in order to stay safe with an up-to-date list of trust­ed CAs when brows­ing the inter­net.

To test encrypt­ed traf­fic on a devel­op­ment serv­er, we need to cre­ate a new self-signed Cer­tifi­cate Author­i­ty, from which we’ll sign and gen­er­ate a brand new serv­er cer­tifi­cate that will be con­fig­ured on the web serv­er to enable encrypt­ed traf­fic.

Start­ing off, from the keys direc­to­ry we pre­vi­ous­ly cre­at­ed, we’ll run this com­mand:

openssl genrsa -des3 -out root.key 4096

This gen­er­ates our root CA’s RSA pri­vate key, encrypts it with a 3DES cypher (it’ll ask you to set a pass­word for encryp­tion — I chose “1234”) and final­ly, stores it in a .key file, which is the new­ly cre­at­ed cer­tifi­cate’s pri­vate key.

Now we’ll have to self-sign it and gen­er­ate a .crt file which con­tains the cer­tifi­cate’s pub­lic key, by run­ning the fol­low­ing com­mand:

openssl req -x509 -new -nodes -key root.key -sha256 -days 1024 -out root.crt

It’ll ask us to intro­duce the pass­word we just chose in the pre­vi­ous step, and also oth­er infor­ma­tion such as your city, state, email…, which are not real­ly that impor­tant for a devel­op­ment cer­tifi­cate. Just fill it in as you like.

Once the root.crt file has been gen­er­at­ed, it needs to be import­ed into every brows­er that will enter our web­site. This will allow the brows­er to trust our recent­ly cre­at­ed CA.

If you’re using Mozil­la Fire­fox, you can go to Set­tings :: Pri­va­cy & Secu­ri­ty :: View Cer­tifi­cates :: Author­i­ties :: Import, and choose your root.crt file to import it.

My self-signed root CA

Once we have our root CA trust­ed by the brows­er, it’s time to cre­ate a serv­er cer­tifi­cate (signed by the root CA we just cre­at­ed) for our Tom­cat serv­er.

II.
Creating a Server Certificate
for our Tomcat Server

To cre­ate the cer­tifi­cate for our web serv­er, we’ll run this com­mand to cre­ate a new pri­vate key, just like when we cre­at­ed one for the root CA:

openssl genrsa -out server.key 2048

Next we need to cre­ate the Cer­tifi­cate Sign­ing Request (CSR) in order to sign our new cer­tifi­cate with our root CA we just cre­at­ed in the pre­vi­ous step.

Run this com­mand to cre­ate a CSR:

openssl req -new -key server.key -out server.csr

It’ll ask us to fill out infor­ma­tion and a pass­word at the end, just like in before. How­ev­er, this time we need to set Common Name as our server’s IP or domain name. If we’re going to use this for local­host, then set it to “local­host” or “127.0.0.1” (with­out quotes).

And that’ll cre­ate a server.csr that we’ll be able to use to gen­er­ate our serv­er cer­tifi­cate.

Final­ly, cre­ate the serv­er cer­tifi­cate using the fol­low­ing com­mand:

openssl x509 -req -in server.csr -CA root.crt -CAkey root.key -CAcreateserial -out server.crt -days 500 -sha256

It’ll ask us for the root.key pass­word we intro­duced ear­li­er.

Once this is done, we’ll now have a server.key (the cer­tifi­cate’s pri­vate key) and a server.crt (the cer­tifi­cate’s pub­lic key).

III.
Enabling TLS in Tomcat with a
Self-Signed Certificate


We need to trans­form our serv­er cer­tifi­cate (.crt & .key pair) into a .p12 file which is one of the key­store for­mats Tom­cat sup­ports (PKCS12):

openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 -name tomcat -CAfile root.crt -chain

This will pro­duce a .p12 file that we can now con­fig­ure in Tom­cat’s server.xml file.

First thing we have to do is com­ment the fol­low­ing <Listener> tag if it’s present in your serv­er con­fig­u­ra­tion file, since we won’t be using the APR libraries to con­fig­ure TLS this time around:

<!--<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />-->

Next, we’ll add our TLS <Connector> tag right after the first <Connector> we encounter in the file:

<Connector 
   port="8443"
   maxThreads="150"
   scheme="https"
   secure="true"
   SSLEnabled="true"
   keystoreFile="C:/apache-tomcat-8.5.51/keys/server.p12"
   keystorePass="1234"
   keystoreType="PKCS12"
   clientAuth="false"
   keyAlias="tomcat"
   sslProtocol="TLS"
/>

Heads Up! keyAlias needs to be set as the -name para­me­ter we used when gen­er­at­ing the .p12 serv­er cer­tifi­cate file.

Now you can launch Tom­cat and vis­it your web­site. You’ll see that the brows­er rec­og­nized the cer­tifi­cate and encrypt­ed our con­nec­tion! 🥳


IV.
Creating a Client Certificate for
Mutual Authentication

We now have a Tom­cat serv­er ful­ly func­tion­al with encrypt­ed traf­fic, so let’s take it a step fur­ther and enable client authen­ti­ca­tion.

First we need to add our root CA to our Java Trust­ed Key­store using keytool (make sure you have your JDK’s /bin/ fold­er added to Path!) so that our serv­er trusts the new client cer­tifi­cates we’re going to cre­ate:

keytool -import -trustcacerts -keystore "C:\Program Files (x86)\Java\jdk1.8.0_192\jre\lib\security\cacerts" -storepass changeit -noprompt -alias localhostroot -file root.crt

Your JDK cac­erts file should be locat­ed (by default) in %JAVA_HOME%\jre\lib\security\cacerts, although it may vary depend­ing on your ver­sion. Read up on this to know where it’s locat­ed.

The next few steps are very sim­i­lar to when we gen­er­at­ed the serv­er cer­tifi­cate: Cre­ate a new pri­vate key, cre­ate a new CSR (Cer­tifi­cate Sign­ing Request) for our root CA, process the CSR into a .crt file and trans­form the .crt and .key pair into a .p12 file we can then import into our brows­er.

Fol­low the same instruc­tions detailed in step 2 (Cre­at­ing a Serv­er Cer­tifi­cate for our Tom­cat Serv­er), but instead of using serv­er.* for file­names, use client.key, client.csr and client.crt.

When cre­at­ing the CSR, the Common Name in this case does not need to be the serv­er domain name since this cer­tifi­cate will be for the client. It should be set to some­thing that iden­ti­fies the user, such as their real name or account user­name.

Once we’ve cre­at­ed the client.crt file, we need to trans­form it into a client.p12 file that browsers allow to import:

openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12

Now that we’ve gen­er­at­ed the client.p12 file, import it into your brows­er (here’s where to do it in Fire­fox, just like when we import­ed the root CA):

Import­ed client cer­tifi­cates in the “Your Cer­tifi­cates” tab

Make sure this time your import it in the “Your Cer­tifi­cates” tab instead of “Author­i­ties”.

Once the cer­tifi­cate has been import­ed, we just need to enable the clientAuth direc­tive in Tom­cat’s server.xml file (by chang­ing clientAuth to true):

<Connector 
   port="8443"
   maxThreads="150"
   scheme="https"
   secure="true"
   SSLEnabled="true"
   keystoreFile="C:/apache-tomcat-8.5.51/keys/server.p12"
   keystorePass="1234"
   keystoreType="PKCS12"
   clientAuth="true"
   keyAlias="tomcat"
   sslProtocol="TLS"
/>

Run (or restart) Tom­cat, and then vis­it your web­site. You’ll be greet­ed with a modal prompt forc­ing you to select a cer­tifi­cate.

You should see the client cer­tifi­cate we just cre­at­ed in the drop­down list. Only cer­tifi­cates that have been signed by serv­er-trust­ed root CAs (i.e. added to the server’s trust­ed key­store, as we did in the begin­ning of this step) will show up here.

V.
Supporting Additional
Certificate Authorities

The client cer­tifi­cates your serv­er accepts (i.e. the ones that appear in the prompt) depend on your trust­ed key­store. In this case, since we’re using Tom­cat and we haven’t con­fig­ured a cus­tom trust­ed key­store (we can do this via extra attrib­ut­es in the <Connector> tag), Tom­cat uses Java’s key­store (the cac­erts file).

To allow cer­tifi­cates emit­ted by oth­er cer­tifi­cate author­i­ties to appear in the prompt, you’ll need to add the CA’s root cer­tifi­cate to your trust­ed key­store (we already did this with our own self-signed root CA, it’s exact­ly the same).

Imag­ine we need to sup­port cer­tifi­cates emit­ted by the Span­ish FNMT (Nation­al Coin and Ding Fac­to­ry of Spain). They emit var­i­ous types of cer­tifi­cates, so they have dif­fer­ent inter­me­di­ate cer­tifi­cates to sat­is­fy each type (sub­or­di­nates to the root CA):

FNMT root cer­tifi­cates

We’d have to obtain the root cer­tifi­cate depend­ing on the type of FNMT cer­tifi­cate we want to sup­port. I myself have an “AC FNMT Usuar­ios” cer­tifi­cate I use for self-rep­re­sen­ta­tion on Span­ish pub­lic admin­is­tra­tion sites. You can check out the root cer­tifi­cate in the “Issued By:” tag:

Once we have the .cer (or .crt) file by down­load­ing it from their web­site (most Cer­tifi­cate Author­i­ties pro­vide down­load links for their root cer­tifi­cates), we can just import it to our trust­ed key­store:

keytool -import -trustcacerts -keystore "C:\Program Files (x86)\Java\jdk1.8.0_192\jre\lib\security\cacerts" -storepass changeit -noprompt -alias fnmtusuarios -file AC_FNMT_Usuarios.cer

Now any FNMT-issued client cer­tifi­cates should appear in the authen­ti­ca­tion prompt drop­down list once you restart Tom­cat and reload your brows­er.

VI.
Wrapping Up!

We con­fig­ured Tom­cat to use our self-signed cer­tifi­cate that we gen­er­at­ed our­selves from our own self-signed root CA. We also enabled client authen­ti­ca­tion so that the user can pro­vide a client cer­tifi­cate when they access the web­site.

Please note that this only valid for test­ing pur­pos­es, for real-world stuff you’d need to use real Cer­tifi­cate Author­i­ties and con­fig­ure their issued cer­tifi­cates.

Stay tuned for anoth­er post on how to retrieve the cer­tifi­cate data pro­gra­mat­i­cal­ly in a Java appli­ca­tion run­ning on Tom­cat.

Thanks for read­ing!


– Kedi