3.Mutual TLS (MTLS)

System Requirements

For this lab you need the following requirements:

In this lab we will use mkcert, a tool that makes it quite easy to setup a local Certificate Authority and create certificates signed by this. Without mkcert you would have to use several java keytool commands that are hard to remember to achieve the same. In case you want to know more on how to do this with keytool then check out the corresponding blog article.

Setup a local Certificate Authority (CA)

To create a local certificate authority (with your own root certificate) use the following mkcert command. Make sure you also have set the JAVA_HOME environment variable if you also want to install the root certificate into the trust store of your JDK.

export JAVA_HOME="$(dirname $(dirname $(readlink -f $(which java))))"
mkcert -install

This leads to an output similar to the following.

Using the local CA at "/home/xxx/.local/share/mkcert" ✨
The local CA is installed in the system trust store! 👍
The local CA is installed in the Firefox and/or Chrome/Chromium trust store! 👍
Sudo password:xxxx
The local CA is now installed in Java's trust store! ☕️

Setup HTTPS (SSL/TLS) for the application

At first you need a valid trusted server certificate. To create a keystore containing the certificate with private/public key pair open a command line terminal then navigate to the subdirectory src/main/resources of this project and use the following command.

mkcert -p12-file server-keystore.p12 -pkcs12 localhost

This should lead to the following output:

Created a new certificate valid for the following names
 - "localhost"

The PKCS#12 bundle is at "server-keystore.p12"

The legacy PKCS#12 encryption password is the often hardcoded default "changeit"

Now you should now find the newly created file server-keystore.p12 in the subdirectory src/main/resources.

To enable SSL/TLS in the spring boot application add the following entries to the application.yml file:

server:
  port: 8443  
  ssl:
    enabled: true
    key-store: classpath:server-keystore.p12
    key-store-type: PKCS12
    key-store-password: changeit
    key-password: changeit

Now (re-)start the application and navigate to https://localhost:8443/library. You should recognize that the server application is now served using a secure Https connection.

Setup the client certificate

First we need of course again a valid trusted client certificate to authenticate our client at the server. Open a command line terminal again and navigate to subdirectory src/main/resources of this project and then use the following command.

mkcert -p12-file client-keystore.p12 -client -pkcs12 peter.parker@example.com

This should lead to the following output:

Created a new certificate valid for the following names
 - "peter.parker@example.com"

The PKCS#12 bundle is at "client-keystore.p12"

The legacy PKCS#12 encryption password is the often hardcoded default "changeit"

This file contains the client certificate including the private/public key pair. To authenticate your web browser for our Spring Boot server application just import the file client-keystore.p12 into the browsers certificate store.

But this is not sufficient, the server application also needs just the certificate (with public key) to be able to validate the client certificate. To achieve this we also need to configure a trust keystore for Spring Boot. You must not use the keystore we just created because the server should not get access to the private key.

Instead we have to create another keystore using the Keystore Explorer that only contains the certificate.

But first we have to export the certificate from the existing keystore client-keystore.p12:

  1. Open keystore with the Keystore Explorer. Select client-keystore.p12 in file dialog.

  2. Then right click on the single entry and select Export/Export certificate chain and then use the

    settings as shown in the figure below (export it to a file named pparker.cer).

Now we can import the exported single certificate into a new keystore.

  1. Open the explorer and then create a new keystore using the menu File/New.

  2. Then chose PKCS#12 as type

  3. Now select the menu Tools/Import Trusted Certificate

  4. Select the exported file pparker.cer from previous section and use peter.parker@example.com as alias when asked

  5. Save the keystore as pparker-trust.p12 and use password changeit when prompted for

Now let's use this new keystore:

server:
  ssl:
    trust-store: classpath:pparker-trust.p12
    trust-store-password: changeit
    client-auth: need

We need the trust store to enable trust between the server application and the client certificate in the web browser. The property client_auth specifies how mandatory the client certificate authentication is. Possible values for this property are:

  • need: The client certificate is mandatory for authentication

  • want: The client certificate is requested but not mandatory for authentication

  • none: The client certificate is not used at all

As final step we have to configure X509 client authentication in com.example.libraryserver.config.WebSecurityConfiguration.java:

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration {

  // ...

  @Configuration
  public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

    private final UserDetailsService userDetailsService;

    public ApiWebSecurityConfigurationAdapter(
        @Qualifier("library-user-details-service") UserDetailsService userDetailsService) {
      this.userDetailsService = userDetailsService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http
          // ...
          .httpBasic(withDefaults())
          .formLogin(withDefaults())
          .headers(
              h -> h.httpStrictTransportSecurity().disable()        
          )
          .x509(
              x -> {
                x.subjectPrincipalRegex("CN=(.*?),");
                x.userDetailsService(userDetailsService);
              });
    }
  }
}

The changes above

  • introduce a reference to the UserDetailsService required for the X509 authentication

  • disable the Http strict transport security header (do not disable this on production, for localhost this can be a problem for testing other

    local web applications not providing a Https connection)

  • configure how to get the principle from the client certificate using a regular expression for the common name (CN)

Now we are ready to restart the application. If you now navigate again to https://localhost:8443/library then you will see a popup asking for the client certificate (you may have already several ones in your browser, so make sure you select the correct one for peter.parker@example.com).

In case you have problems with forcing the browser to redirect to Https for apps not providing Https on localhost just go to How to Clear HSTS Settings on Chrome, Firefox and IE Browsers and follow instructions for your web browser.

Reference Documentation

For further reference, please consider the following sections:

Guides

The following guides illustrate how to use some features concretely:

Last updated