2

I'm working with JRE 1.8.0_51 (I cannot change this), which does not include the root certificate for Let's Encrypt in lib/security/cacerts (it is added in 1.8.0_141)

I need to add the certificate at runtime, and I have found this code to do that:

        try {
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            InputStream keyStoreStream = getClass().getClassLoader().getResourceAsStream("j51_le_ca_cert.jks");
            keyStore.load(keyStoreStream, "changeit".toCharArray());
            trustManagerFactory.init(keyStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustManagers, null);
            SSLContext.setDefault(sc);

        } catch (Exception e) {
            e.printStackTrace();
        }

This runs without throwing any error, but causes one of following things to happen:

  • The LE certificate is correctly installed, but overwrites all existing certificates
  • The LE certificate is not installed and all existing certificates remain

Which of those two events happens varies, seemingly arbitrarily, based on the system and environment in which it is running.

How can I reliably install the certificate alongside the existing certificates?

pjones123
  • 355
  • 2
  • 7
  • Find a way to upgrade Java in production. Saying "I'm working with JRE 1.8.0_51 (I cannot change this)" is not realistic. Java 17 is now available. At some point you need an upgrade channel. Your operating system is probably insecure too. – Elliott Frisch Sep 19 '21 at 15:40
  • [This](http://knowledge-oracle.blogspot.com/2009/02/import-private-key-and-certificate-in.html) may be useful to you. – Arvind Kumar Avinash Sep 19 '21 at 15:47
  • @ElliottFrisch That simply isn't possible in this situation. If it were a possibilty then I would have done that, but in this scenario I must work with this version. – pjones123 Sep 19 '21 at 15:47
  • You can also check [this](https://stackoverflow.com/q/18889058/10819573) one. – Arvind Kumar Avinash Sep 19 '21 at 15:49
  • The problem may be at `SSLContext.getInstance("SSL");`. This may only be affecting one or some of the SSL/TLS versions you need it to. See the discussion on [SSLContext algorithm names](https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext). I think you may need to repeat this initialization for each specific version of TLS you need to support, i.e. `SSLContext.getInstance("TLSv1.2");` As for the other problem, what exactly is contained in `j51_le_ca_cert.jks`? – President James K. Polk Sep 19 '21 at 17:42

1 Answers1

1

Based on your question it looks like you have the following requirements:

  1. Not possible to upgrade Java, need to stay at v1.8.051
  2. Use the default jdk truststore (cacert)
  3. Add additional trusted certificate to the existing ones during runtime

I think this is possible. What you can do is create two separate TrustManagers and combined them as mentioned here: Registering multiple keystores in JVM. You just need to create a trustmanager from the default jdk truststore (cacert) and one for your own truststore containing the one with lets encrypt and merge that with a CompositeTrustManager as mentioned in that topic. This special TrustManager can be used to create a SSLContext.

I have extracted the code snippet into a library, so if you don mind to use an additional library you can try out the following snippet:

import nl.altindag.ssl.SSLFactory;

import javax.net.ssl.SSLContext;

public class App {

    public static void main(String[] args) {
        SSLFactory sslFactory = SSLFactory.builder()
                .withTrustMaterial("j51_le_ca_cert.jks", "changeit".toCharArray())
                .withDefaultTrustMaterial()
                .withSslContextAlgorithm("SSL")
                .build();

        SSLContext sslContext = sslFactory.getSslContext();
        SSLContext.setDefault(sslContext);
    }

}

You can add the library with the following snippet:

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>sslcontext-kickstart</artifactId>
    <version>7.0.0</version>
</dependency>

See here for the full documentation and example usages: GitHub - SSLContext Kickstart

I think it is better to upgrade your JDK, but sometimes it is just not possible, so I hope this alternative solution will work for you.

Hakan54
  • 3,121
  • 1
  • 23
  • 37