Introduction

This post tries to answer a question I get quite frequently : I’ve got my web application running, now how do I secure access to it through HTTPS.
The answer to this, is quite simple: there is nothing to do (at the application level) to make this work, everything is done by the underlying server components.

In this post I will consider two scenarii for adding HTTPS support on a given web application :

  • The users access the application directly on the tomcat server using an url similar to https://tomcat-server.local:8043/petclinic. In this case the Tomcat server provides directly HTTPS support through the appropriate connector.
  • The application is behind an apache reverse proxy, the user access the application using an url similar to https://apache-server.local/petclinic. In this it is the front Apache server that provides both HTTPS support and reverse proxying to the backend tomcat server. HTTPS support is provided through the httpd mod_ssl module.

Generating your certificates

If you already have your certificates, you can skip this section, otherwise read on to see how to generate the necessary files.

The prerequisite for setting up HTTPS is to have a private key and public certificate for your server. The private key is a RSA key known only to the server. This key is used to decrypt incoming messages. The public certificate is an x509 certificate publicly available. This is used by remote client to encrypt the messages. Also note that it is good practice to have your public certificates signed by a trusted Certificate Authority or CA.

For the purposes of this example, we shall generate the following set of files :
* The root CA private key and self signed public certificate. For test purposes we will act as our own root CA. In a production scenario, these are provided by an external body such as CaCert.org or Verisign to name but a few

  • The server private RSA key and public x509 certificate signed by our root CA certificate

These files are created using openssl as following:

#-- generate the root CA private key
openssl genrsa \
	-out ssl.ca/ca.key  2048

#-- generate a self signed certificate for the root CA
openssl req -new \
	-sha1 -x509 -days 365 \
	-key ssl.ca/ca.key -out ssl.ca/ca.crt \
	-subj "/O=Company/OU=Department/CN=ca.example.com"

#-- generate the private key for the server
openssl genrsa \
	-out ssl.key/server.key 2048

#-- create a certificate signing request for the server
openssl req -new \
	-key ssl.key/server.key -out tmp/server.csr \
	-subj "/O=Company/OU=Department/CN=server"

#-- generate a certificate for the server signed by the root CA
openssl x509 -req \
	-in tmp/server.csr -out ssl.cert/server.crt \
	-CA ssl.ca/ca.crt -CAkey ssl.ca/ca.key -CAcreateserial \
	-days 365

Configuring the HTTPS Connector in Tomcat

HTTPS support in Tomcat is configured in $CATALINA_HOME/conf/server.xml through an HTTP Connector with SSL support. The steps to configure this are:
* Generate a keystore which contain both the private key and the public certificate

  • In $CATALINA_HOME/conf/server.xml configure an HTTP connector with SSL support

Generate the keystore

The Tomcat HTTP connector does not use the RSA private key and x509 public certificate directly. Instead, it requires the use of a keystore which contains the private key and the public certificate chain. Java now supports two format for the keystores, the native JKS format and the standard PKCS12 format.
In this example, we’ll use openssl to generate the keystore in the PKCS12 format. The command is:

openssl pkcs12 -export -out ssl.p12/server.p12 -in ssl.cert/server.crt -inkey ssl.key/server.key  -name "server"  -password pass:secret

And place the resulting server.key file in the conf/ssl/ directory of the tomcat installation

Configure the HTTPS connector

In $CATALINA_HOME/conf/server.xml add the following Connector element as a child of the Service element:

<?xml version='1.0' encoding='utf-8'?>

<Server port="8005" shutdown="SHUTDOWN">
		:
		:
	<Service name="Catalina">
  			:
			:
		<Connector	port="8043"  protocol="HTTP/1.1"
			SSLEnabled="true"
			scheme="https"
			secure="true"
			clientAuth="false"
			sslProtocol="TLS"
			keystoreFile="conf/ssl/server.p12"
			keystorePass="secret"
			keystoreType="PKCS12"
			/>
		:
		:
	</Service>
</Server>

About mutual authentication

If your security policy requires mutual authentication to access the application, there are additional steps:
* Get your client public certificates and generate a JKS truststore out of it

  • Modify the HTTPS connector configuration to set clientAuth to true and specify the location of the truststore file

In the case of this example, we can add an HTTPS connector that sets mutual authentication as following

Generate the truststore

Given the client1.crt, client2.crt, client3.crt files in the ssl.crt directory, we can concatenate the three certificates in a JKS truststore using the keytool command provided by the JDK as following:

keytool -import -alias client1  -file ssl.cert/client1.crt -storepass secret -noprompt -keystore truststore.jks
keytool -import -alias client2  -file ssl.cert/client2.crt -storepass secret -noprompt -keystore truststore.jks
keytool -import -alias client3  -file ssl.cert/client3.crt -storepass secret -noprompt -keystore truststore.jks

Add the HTTPS connector

The tomcat configuration for the HTTPS connector with mutual authentication enabled becomes:

<Connector	port="8044"  protocol="HTTP/1.1"
	SSLEnabled="true"
	scheme="https"
	secure="true"
	clientAuth="true"
	sslProtocol="TLS"
	keystoreFile="conf/ssl/server.p12"
	keystorePass="secret"
	keystoreType="PKCS12"
	truststoreFile="conf/ssl/truststore.jks"
	truststorePass="secret"
	truststoreType="JKS"/>

With this configuration only those client that can provide a public certificate that is also located in the server’s truststore can actually connect to the server.

Adding HTTPS support to the Apache reverse proxy

With this scenario, the tomcat web application is hidden behind an Apache httpd web server which acts as a reverse proxy. Also in this example, we further consider the case where the reverse proxy connects to the ajp port of the tomcat server.
With this setup, HTTPS behaviour is provided by the frontal apache web server. Adding SSL support is then a case of configuring the reverse proxy in an SSL enabled virtual host.

To achieve this use the following steps:
* Copy the server private key to $APACHE_HOME/ssl.key/server.key
* Copy the server public certificate to $APACHE_HOME/ssl.crt/server.crt
* Copy the root ca chain to $APACHE_HOME/ssl.crt/ca.crt
* Create your SSL VirtualHost as following:

LoadModule ssl_module		/usr/lib64/apache2-prefork/mod_ssl.so
LoadModule proxy_module		/usr/lib64/apache2-prefork/mod_proxy.so
LoadModule proxy_ajp_module	/usr/lib64/apache2-prefork/mod_proxy_ajp.so

NameVirtualHost *:443

<VirtualHost *:443>
	SSLEngine on

	SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL

	# Server certificate
	SSLCertificateFile /etc/apache2/ssl.crt/server.crt

	# Server private key
	SSLCertificateKeyFile /etc/apache2/ssl.key/server.key

	# Server Certificate Chain
	SSLCertificateChainFile /etc/apache2/ssl.crt/ca.crt

	# Certificate Authority
	SSLCACertificateFile /etc/apache2/ssl.crt/ca.crt

	<Proxy *>
		Order deny,allow
		Allow from all
	</Proxy>

	ProxyPass           /petclinic	ajp://tomcat-server.local:8009/petclinic
	ProxyPassReverse    /petclinic	ajp://tomcat-server.local:8009/petclinic

</VirtualHost>
Advertisements