Java Spring Boot With PEM-based TLS on-the-fly

print

I spent some time in a company that had been running many Java “microservices” – a code word for Java Spring Boot servers. One of the problems was how to efficiently manage zillions of JKS/P12 files.

Developers were used to simply create JKS/P12 files wherever they needed them. The problem was that this was not practically manageable at scale. It ultimately increased the overall cost of ownership. It also made the infrastructure fragile as it was near impossible to keep track of all TLS certificates.

Defining a New Pattern

In this particular case, one of the environments was the AWS cloud with Private CAs. We have eventually managed to integrate these Private CAs into the internal PKI (public key infrastructure).

The next question was – how can we leverage this new capability to improve resiliency of the company infrastructure. The final approach was to centralize certificate issuance and provide all necessary certificates as a “platform resource”. The next step was to get rid of local files and make applications pull private keys and certificates when they changed.

The main problem turned out to be inability of “normal” developers to embrace the new key management concept. At the end, I had to write example code to demonstrate that it was possible.

Example Code

The code is at https://gitlab.com/smartarcs/spring-boot-tls . The project is basically a copy of the default Spring Boot project with just one new file:

https://gitlab.com/smartarcs/spring-boot-tls/-/blob/master/complete/src/main/java/com/example/springboot/CustomContainer.java

This example doesn’t use AWS API but has embedded PEM text strings that contain certificates and encrypted private key – exactly the information you get from AWS API – AWS Certificate Manager – Exporting Certificate.

  • result.getCertificate()
  • result.getCertificateChain()
  • result.getPrivateKey()

We decided to use IAM to manage access control and as such the password was empty. Obviously, you can further tighten up the access by using a secrets manager (whether Vault, SecretsManager, or anything else).

The code uses PemReader and can deal with:

  • “ENCRYPTED PRIVATE KEY”
  • “PRIVATE KEY”; AND
  • “CERTIFICATE”

All this is implemented in a class that overloads customise() of TomcatServletWebServerFactory. where I created a new SslStoreProvider that returns our on-the-fly created keyStore.

KeyStore keystore = KeyStore.getInstance("pkcs12");
                keystore.load(null);
                keystore.setKeyEntry("alias", privateKey, "".toCharArray(), certs.toArray(new Certificate[0]));

This is then called from the same customize() method:

        factory.setSslStoreProvider(sslProvider);
        Ssl ssl = new Ssl();
        String[] protocols = {"TLSv1.2", "TLSv1.3"};
//        ssl.setEnabledProtocols(protocols);
//        ssl.setEnabled(true);
        factory.setSsl(ssl);
        factory.setPort(8081); // if you want to set own port

What is missing from this Java Spring Boot

  1. instead of waiting for the application restart, it would be nice to periodically check for any changes in the certificate and automatically load a new version without downtime.
  2. client authentication for mutual TLS that would be based on the domain name. For some reason, Spring Boot locks mTLS to a certificate, instead to a domain name of a trusted (i.e., issued by a known CA) certificate.

Leave a Reply

Your email address will not be published.