Home > database >  Add certificates to global Java truststore in code (programatically)
Add certificates to global Java truststore in code (programatically)

Time:02-02

I have a Kubernetes Deployment of my Spring Boot application where I used to update global Java cacerts using keytool at the bootstrap:

keytool -noprompt -import -trustcacerts -cacerts -alias $ALIAS -storepass $PASSWORD

However, I need to make the container immutable using the readOnlyRootFilesystem: true in the securityContext of the image in my Deployment. Therefore, I cannot update the cacert like that with additional certificates to be trusted.

Additional certificates that should be trusted are provided as environment variable CERTS.

I assume that the only proper way would be to do this programmatically, for example during @PostConstruct in the Spring Boot component.

I was looking into some examples how to set the global truststore in code, but all of them refer to update the cacerts and then save it to filesystem, which does not work for me.

Some examples use System.setProperty("javax.net.ssl.trustStore", fileName);, but this does not work either on the read-only filesystem, where I cannot update file.

Another examples suggest to use X509TrustManager, but if I understood correctly, this does not work globally.

Is there any way in Java or Spring Boot to update global truststore in general programmatically so every operation in the code will use and I do not have to implement something like TrustManager to every connection? My goal is to have it imported at the begging (similar like it is done using shell and keytool). Without touching the filesystem, as it is read-only.

CodePudding user response:

You can use the following approach to update the Java truststore programmatically without modifying the read-only filesystem:

  1. Create a KeyStore object in your code.
  2. Load the existing truststore into the KeyStore object using the truststore password.
  3. Parse the environment variable CERTS and add the certificates to the KeyStore object.
  4. Use the javax.net.ssl.TrustManagerFactory to create a TrustManagerFactory with the KeyStore.
  5. Use the TrustManagerFactory to initialize a SSLContext with the trustmanager.
  6. Use the SSLContext.init() method to set the SSL context as the default for all SSL connections.

You can achieve this in a Spring Boot component:

@PostConstruct
public void configureGlobalTrustStore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException {
    KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    InputStream inputStream = getClass().getResourceAsStream("/cacerts");
    trustStore.load(inputStream, "changeit".toCharArray());
    inputStream.close();

    String certString = System.getenv("CERTS");
    if (certString != null) {
        String[] certArray = certString.split(" ");
        for (int i = 0; i < certArray.length; i  ) {
            InputStream certInput = new ByteArrayInputStream(Base64.getDecoder().decode(certArray[i]));
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(certInput);
            certInput.close();
            trustStore.setCertificateEntry("cert-"   i, cert);
        }
    }

    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init(trustStore);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

    SSLContext.setDefault(sslContext);
}

This way, every SSL connection in your code will use the updated truststore, without having to configure it for each individual connection.

  • Related