Mutual Authentication in Tomcat using Self-Signed Certificatescalendar_today Posted 9 months ago · 10 minute-read · Technology
Due to an application I’m developing at work, I’ve had to try out client authentication in a Tomcat 8 environment. What this does is it enables mutual authentication between the client and the server.
This is mostly used in enterprise systems where users must identify themselves by using custom CA-signed certificates, for example in banking or public administration.
When mutual authentication is enabled and properly configured on a web server, users visiting the website are greeted with a prompt that asks them choose a certificate to present as identification:
Let’s check out all the required steps needed in order to configure this technology and enable it in a Tomcat 8 environment running on Windows.
Table of Contents
- Getting Started
- Creating a Self-Signed Certificate Authority
- Creating a Server Certificate for our Tomcat Server
- Enabling TLS in Tomcat with a Self-Signed Certificate
- Creating a Client Certificate for Mutual Authentication
- Supporting Additional Certificate Authorities
- Wrapping Up!
- Install the required OpenSSL binaries from here: https://slproweb.com/products/Win32OpenSSL.html.
You’ll want the 64-bit version.
- Add the bin folder to your path environment variable for convenience. The path to add should be this one:
- Also add your JDK bin folder to your path, again, for convenience:
C:\Program Files (x86)\Java\jdk1.8.0_192\bin
(change to your JDK location)
- I created a folder named “keys” in my root Tomcat folder
C:\apache-tomcat-8.5.51\keysto handle all the certificate stuff, you can do this too. Just make sure you have a place dedicated to this since we’ll be generating quite a few files.
cmdas an administrator and
cdto the “keys” folder in Tomcat.
Creating a Root Certificate Authority
Root Certificate Authorities (CA from now on) are used to generate new certificates that are configured on the server in order to allow secure encrypted traffic. If we head to google.com and check out the certificate they’re serving us, we can see their root CA:
In Google’s case, they have a CA named
GTS CA 1O1 which signed their certificate for use on
CAs root certificates come bundled with software. For example, browsers like Google Chrome or Mozilla Firefox come with a set of trusted root CAs.
With software updates, root CAs are added and removed from the trusted certificate store, so it’s important that you keep your browsers updated in order to stay safe with an up-to-date list of trusted CAs when browsing the internet.
To test encrypted traffic on a development server, we need to create a new self-signed Certificate Authority, from which we’ll sign and generate a brand new server certificate that will be configured on the web server to enable encrypted traffic.
Starting off, from the keys directory we previously created, we’ll run this command:
openssl genrsa -des3 -out root.key 4096
This generates our root CA’s RSA private key, encrypts it with a 3DES cypher (it’ll ask you to set a password for encryption — I chose “1234”) and finally, stores it in a
.key file, which is the newly created certificate’s private key.
Now we’ll have to self-sign it and generate a
.crt file which contains the certificate’s public key, by running the following command:
openssl req -x509 -new -nodes -key root.key -sha256 -days 1024 -out root.crt
It’ll ask us to introduce the password we just chose in the previous step, and also other information such as your city, state, email…, which are not really that important for a development certificate. Just fill it in as you like.
Once the root.crt file has been generated, it needs to be imported into every browser that will enter our website. This will allow the browser to trust our recently created CA.
If you’re using Mozilla Firefox, you can go to Settings :: Privacy & Security :: View Certificates :: Authorities :: Import, and choose your
root.crt file to import it.
Once we have our root CA trusted by the browser, it’s time to create a server certificate (signed by the root CA we just created) for our Tomcat server.
Creating a Server Certificate
for our Tomcat Server
To create the certificate for our web server, we’ll run this command to create a new private key, just like when we created one for the root CA:
openssl genrsa -out server.key 2048
Next we need to create the Certificate Signing Request (CSR) in order to sign our new certificate with our root CA we just created in the previous step.
Run this command to create a CSR:
openssl req -new -key server.key -out server.csr
It’ll ask us to fill out information and a password at the end, just like in before. However, this time we need to set
Common Name as our server’s IP or domain name. If we’re going to use this for localhost, then set it to “localhost” or “127.0.0.1” (without quotes).
And that’ll create a
server.csr that we’ll be able to use to generate our server certificate.
Finally, create the server certificate using the following command:
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 password we introduced earlier.
Once this is done, we’ll now have a
server.key (the certificate’s private key) and a
server.crt (the certificate’s public key).
Enabling TLS in Tomcat with a
We need to transform our server certificate (.crt & .key pair) into a
.p12 file which is one of the keystore formats Tomcat supports (PKCS12):
openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 -name tomcat -CAfile root.crt -chain
This will produce a
.p12 file that we can now configure in Tomcat’s
First thing we have to do is comment the following
<Listener> tag if it’s present in your server configuration file, since we won’t be using the APR libraries to configure 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" />
keyAlias needs to be set as the
-name parameter we used when generating the
.p12 server certificate file.
Now you can launch Tomcat and visit your website. You’ll see that the browser recognized the certificate and encrypted our connection! 🥳
Creating a Client Certificate for
We now have a Tomcat server fully functional with encrypted traffic, so let’s take it a step further and enable client authentication.
First we need to add our root CA to our Java Trusted Keystore using
keytool (make sure you have your JDK’s /bin/ folder added to Path!) so that our server trusts the new client certificates we’re going to create:
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 cacerts file should be located (by default) in
%JAVA_HOME%\jre\lib\security\cacerts, although it may vary depending on your version. Read up on this to know where it’s located.
The next few steps are very similar to when we generated the server certificate: Create a new private key, create a new CSR (Certificate Signing Request) for our root CA, process the CSR into a
.crt file and transform the .crt and .key pair into a
.p12 file we can then import into our browser.
Follow the same instructions detailed in step 2 (Creating a Server Certificate for our Tomcat Server), but instead of using server.* for filenames, use client.key, client.csr and client.crt.
When creating the CSR, the
Common Name in this case does not need to be the server domain name since this certificate will be for the client. It should be set to something that identifies the user, such as their real name or account username.
Once we’ve created the
client.crt file, we need to transform 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 generated the
client.p12 file, import it into your browser (here’s where to do it in Firefox, just like when we imported the root CA):
Make sure this time your import it in the “Your Certificates” tab instead of “Authorities”.
Once the certificate has been imported, we just need to enable the
clientAuth directive in Tomcat’s
server.xml file (by changing
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) Tomcat, and then visit your website. You’ll be greeted with a modal prompt forcing you to select a certificate.
You should see the client certificate we just created in the dropdown list. Only certificates that have been signed by server-trusted root CAs (i.e. added to the server’s trusted keystore, as we did in the beginning of this step) will show up here.
The client certificates your server accepts (i.e. the ones that appear in the prompt) depend on your trusted keystore. In this case, since we’re using Tomcat and we haven’t configured a custom trusted keystore (we can do this via extra attributes in the
<Connector> tag), Tomcat uses Java’s keystore (the cacerts file).
To allow certificates emitted by other certificate authorities to appear in the prompt, you’ll need to add the CA’s root certificate to your trusted keystore (we already did this with our own self-signed root CA, it’s exactly the same).
Imagine we need to support certificates emitted by the Spanish FNMT (National Coin and Ding Factory of Spain). They emit various types of certificates, so they have different intermediate certificates to satisfy each type (subordinates to the root CA):
We’d have to obtain the root certificate depending on the type of FNMT certificate we want to support. I myself have an “AC FNMT Usuarios” certificate I use for self-representation on Spanish public administration sites. You can check out the root certificate in the “Issued By:” tag:
Once we have the .cer (or .crt) file by downloading it from their website (most Certificate Authorities provide download links for their root certificates), we can just import it to our trusted keystore:
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 certificates should appear in the authentication prompt dropdown list once you restart Tomcat and reload your browser.
We configured Tomcat to use our self-signed certificate that we generated ourselves from our own self-signed root CA. We also enabled client authentication so that the user can provide a client certificate when they access the website.
Please note that this only valid for testing purposes, for real-world stuff you’d need to use real Certificate Authorities and configure their issued certificates.
Stay tuned for another post on how to retrieve the certificate data programatically in a Java application running on Tomcat.
Thanks for reading!