In a rather big application there is the need to connect to various https://... hosts (web services, rest services, html scraping etc).

Quite a few of the hosts suffers from various ssl problems (self signed cert, broken cert chain) or simply the root cert is not in the system wide trusted CA list.

Until now the problem was "solved" by setting an "all trusting" X509TrustManager in the appropriate places.

Now I have to stop this practice ( security audit... ).

I can not change the environment's CA list.

I would like to add the few problematic leaf-certs and root-certs centrally. My first thought was to create a JKS keystore with the certs and register it with the following JVM option:

-Djavax.net.ssl.trustStore=additional_CAs_truststore.jks

However, the result is that the system-wide certs are not considered then, and the application can only connect to the "problematic" sites, and not for example to "https://google.com".

Then I saw an example of a "delegating trust manager" which looks like this:

public class TrustManagerDelegate implements X509TrustManager {
    private final X509TrustManager mainTrustManager;
    private final X509TrustManager fallbackTrustManager;

    public TrustManagerDelegate(X509TrustManager mainTrustManager, X509TrustManager fallbackTrustManager) {
        this.mainTrustManager = mainTrustManager;
        this.fallbackTrustManager = fallbackTrustManager;
    }

...

    @Override
    public void checkServerTrusted(final X509Certificate[] x509Certificates, final String authType) throws CertificateException {
        try {
            mainTrustManager.checkServerTrusted(x509Certificates, authType);
        } catch(CertificateException ignored) {
            this.fallbackTrustManager.checkServerTrusted(x509Certificates, authType);
        }
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return this.fallbackTrustManager.getAcceptedIssuers();
    }
}

With that trust manager, my code would look like this:

    public void run(String... args) throws Exception {

        final TrustManagerFactory javaDefaultTrustManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        javaDefaultTrustManager.init((KeyStore) null);

        final TrustManagerFactory additionalCaTrustManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        KeyStore ks = KeyStore.getInstance("JKS");
        InputStream is = new FileInputStream("additional_CAs_truststore.jks");
        ks.load(is, "changeit".toCharArray());
        is.close();
        additionalCaTrustManager.init(ks);

        for(X509Certificate x509Certificate : ((X509TrustManager)javaDefaultTrustManager.getTrustManagers()[0]).getAcceptedIssuers()) {
            System.out.println("Accepted issuer from java default trust manager: " + x509Certificate.getIssuerX500Principal());
        }
        for(X509Certificate x509Certificate : ((X509TrustManager)additionalCaTrustManager.getTrustManagers()[0]).getAcceptedIssuers()) {
            System.out.println("Accepted issuer from additional trust manages: " + x509Certificate.getIssuerX500Principal());
        }

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(
                null,
                new TrustManager[]{
                        new TrustManagerDelegate(
                                (X509TrustManager)additionalCaTrustManager.getTrustManagers()[0],
                                (X509TrustManager)javaDefaultTrustManager.getTrustManagers()[0]
                        )
                },
                secureRandom
        );
        SSLContext.setDefault(sslContext);

        {
            String content = Request.get("https://google.com").execute().returnContent().asString();
            System.out.println("google.com content length: "  + content.length());
        }

        {
            String content = Request.get("https://untrusted-root.badssl.com/").execute().returnContent().asString();
            System.out.println("untrusted-root.badssl.com content length: " + content.length());
        }

        {
            String content = Request.get("https://teszt.kv.gov.hu/").execute().returnContent().asString();
            System.out.println("teszt.kv.gov.hu content length: " + content.length());
        }

    }

The above works, and I rather like it.

But I would like to know if there is a better / more straightforward way to set up additional trusted CA certs on top of the system wide set of CA certs.

Here is a working demo:

https://github.com/riskop/merging_trust_manager_demo


After the ideas received I created another demo which shows both the "merging" and the "delegating" strategies:

https://github.com/riskop/merging_or_delagating_trustmanager_demo

3 Replies 3

https://www.baeldung.com/java-custom-truststore article with a very similar code approach. Suggests another simple option: merge the system keystore with yours. (presumably needs to be re-run after system updates)

What you have is simple and, more importantly, works. And if the system trust store gets updated, you don't need to refresh any copy.

Code to create a trust manager with all trusted CAs merged will be a lot more complex as it would have to explicitly copy CA certs from one trust store to another. So you'd still have to load two trust stores, and instead of simply checking a server's cert against them in sequence using five lines of easy-to-understand code, you'd have to write a lot more code to copy certs.

A try-catch is a lot faster than any TLS negotiation, so any performance difference is going to be negligible, if you can measure it at all.

I would load my custon KeyStore as well as the Java system keystore and then merge them at run-time into one KeyStore (copy certs from system keystore into the custom). And finally use it to initialize a standard TrustManager:

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

Do that when your app starts so you only have to do it once and then you can use the TrustManager where ever you want.

Your Reply

By clicking “Post Your Reply”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.