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?
Advertisement
Answer
Based on your question it looks like you have the following requirements:
- Not possible to upgrade Java, need to stay at v1.8.051
- Use the default jdk truststore (cacert)
- 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.