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
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
$CATALINA_HOME/conf/server.xmlconfigure 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
$CATALINA_HOME/conf/server.xml add the following
Connector element as a child of the
<?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
clientAuthto 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
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>