Using SSL to Secure Tomcat with HTTPS

Learn how to configure the Tomcat web server with SSL.

“Help someone, you earn a friend. Help someone too much, you make an enemy.” ― Erol Ozan

1. Introduction

After covering SSL-enabling the Apache web server and the Nginx web server using an SSL certificate, let us now find out how to secure Apache Tomcat with HTTPS. Tomcat is an open-source java web server intended as a reference implementation of Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies. For the sake of this article, we shall install version 8.5.28 of the Tomcat web server. This version is the latest in the 8.0 series of the server. Installation of the next version, 9.0, should be similar.

Before starting installation or configuration, you will need to obtain an SSL certificate from a valid certifying authority. See this article for more details regarding obtaining an SSL certificate.

2. Install Java

Obviously java is required on the server if you need to run Tomcat. We install java version 8 on the server as follows. This is for an Ubuntu Linux server.

Run the following commands as root. It downloads the latest version of java available (at the time of writing this article, Feb 2018): 8.0_161

mkdir -p ~/download
cd ~/download
wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" 'http://download.oracle.com/otn-pub/java/jdk/8u161-b12/2f38c3b165be4555a1fa6e98c45e0808/jdk-8u161-linux-x64.tar.gz'

The following commands setup this version of java as the default on your Ubuntu system. It also installs a profile update in /etc/profile.d/java.sh. Source this file in when you need JAVA_HOME set appropriately.

mkdir -p /usr/lib/jvm
cd /usr/lib/jvm
tar xvzf ~/download/jdk-8u161-linux-x64.tar.gz
JAVADIR=jdk1.8.0_161
chown -R root:root $JAVADIR
update-alternatives --install /usr/bin/java java /usr/lib/jvm/$JAVADIR/bin/java 100
update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/$JAVADIR/bin/javac 100
update-alternatives --display javac
echo "export JAVA_HOME=/usr/lib/jvm/$JAVADIR" >| /etc/profile.d/java.sh

3. Install Tomcat

Let us now install Tomcat version 8.5.28. Download the Tomcat binary distribution, unpack it and move it to /etc/tomcat.

cd ~/download
wget 'http://www-eu.apache.org/dist/tomcat/tomcat-8/v8.5.28/bin/apache-tomcat-8.5.28.tar.gz'
tar xvzf apache-tomcat-8.5.28.tar.gz
mv apache-tomcat-8.5.28 /etc/tomcat

4. Start Tomcat

Let us now start Tomcat. What we are doing is testing the out the out-of-box distribution before we make changes to the configuration.

By default, Tomcat listens on port 8080 and does not have SSL enabled. Start Tomcat as follows and check that it is running.

/etc/tomcat/bin/startup.sh

Check what ports have been opened.

netstat -an -t tcp -p | grep LISTEN
# prints
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      10898/java
tcp        0      0 127.0.0.1:8005          0.0.0.0:*               LISTEN      10898/java
...

The second port 8005 is a shutdown port. Tomcat listens on this port for shutdown requests. It is opened only on the localhost interface.

Navigate to the website address on port 8080 and check that everything is working.

You can now shutdown Tomcat so we can re-configure it.

/etc/tomcat/bin/shutdown.sh

5. Convert PEM Certificate to JKS

It is possible that your certificate (that you have obtained after any required verification) is in PEM format. This will need to be converted to JKS format for use with Tomcat. Let us see how to do this.

5.1. PEM Format and RSA Key

The PEM format is the most common format used by CAs and looks like this.

example.com.cer:

-----BEGIN CERTIFICATE-----
MIIFDTCCA/WgAwIBAgISA9Nv6Uw1MoDf0Jl9Mbc7LfxHMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
...
-----END CERTIFICATE-----

And the associated RSA private key looks like this:

example.com.key:

-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDRN+NeIUjwTxkE
nxKPkH8AXKGVSPTmYjAsW4j0fWeYEFjFgyfpQIhVEYc0OC2eYhNyCtasfzGjrIHN
...
-----END PRIVATE KEY-----

5.2. Removing Password from RSA Private Key

Let us remove the encryption from the RSA private key. This is because we will convert the key to JKS format which has its own password. The following command will remove the password from the key and write the unencrypted private key into example.com.keynopass (which we will use below).

openssl rsa -in example.com.key -out example.com.keynopass

5.3. Convert PEM to PKCS12

PKCS #12 is a standard format for storing many cryptography entities in a single file. Here we use it to bundle the private key with the SSL certificate.

cat example.com.keynopass example.com.cer > example.com.keyandcer.pem
openssl pkcs12 -export -in example.com.keyandcer.pem -out example.com.pkcs12

We have now ended up with the PKCS #12 file example.com.pkcs12.

5.4. Convert PKCS12 to JKS

Let us now convert the PKCS #12 file to Java KeyStore(JKS) format. This is what Tomcat uses as the SSL certificate store.

This command uses the java tool called keytool which is installed along with the JDK or JRE.

/usr/lib/jvm/jdk1.8.0_161/bin/keytool -importkeystore -srckeystore example.com.pkcs12 -srcstoretype pkcs12 -destkeystore example.com.jks

This final keystore file (example.com.jks) is what we use with Tomcat.

6. Tomcat Re-Configuration

Let us now re-configure Tomcat to listen on port 80 for the insecure web server, and port 443 for the SSL-secured part.

Change from port 8080 (the default) to port 80 by finding and changing the following section in /etc/tomcat/conf/server.xml:

    <Connector port="80" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="443" />

Configure SSL by un-commenting the following section and updating it as shown. If the section does not exist right below the above Connector statement, enter it.

    <Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true">
        <SSLHostConfig>
            <Certificate certificateKeystoreFile="ssl/example.com.jks"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>

6.1. Testing

Let us now restart the Tomcat server to verify everything is kosher.

/etc/tomcat/bin/shutdown.sh
/etc/tomcat/bin/startup.sh

That should get Tomcat going. Now hit the website to check (or use curl). It should be working.

What, it is not?!? What happended?

Well, it turns out Tomcat is very slow to start up in certain situations. In fact, on my Ubuntu 16.04 system, here is the appropriate startup message.

26-Feb-2018 06:09:05.847 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 204197 ms

A start-up time of 204197 ms? That is almost 3 minutes and 25 secs. If you are serving a busy web site with Tomcat, imagine getting knocked off for this long.

What is the reason for this?

6.2. Use /dev/urandom

Turns out Tomcat is initializing the cryptography parts of the server by reading some random bytes through SecureRandom. And SecureRandom is, by default, configured (on Linux anyway) to read from /dev/random. This causes Tomcat to wait till the required number of bytes are available.

This can be checked by verifying the value of the variable securerandom.source in $JAVA_HOME/jre/lib/security/java.security. On my system it is set like this:

securerandom.source=file:/dev/random

One solution is to use /dev/urandom instead of /dev/random. Make this change in $JAVA_HOME/jre/lib/security/java.security:

securerandom.source=file:/dev/urandom

Now re-start Tomcat and check again. The slow startup problem should be gone.

Is it not gone yet? Please let me know in the comments below.

Conclusion

And that covers enabling the Tomcat web server with an SSL certificate. First we installed Oracle’s Java. Next we converted the certificate and RSA private key in PEM format to Java KeyStore (JKS). And then we configured Tomcat with the JKS path.

You can also learn how to add an SSL certificate to the Apache web server or Nginx web server.