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