I am distributing a library jar for internal clients, and the library includes a certificate which it uses to call a service that is also internal to our network.
The trust manager is set up as follows
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); KeyStore keystore = KeyStore.getInstance("JKS"); InputStream keystoreStream = clazz.getClassLoader().getResourceAsStream("certs.keystore"); // (on classpath) keystore.load(keystoreStream, "pa55w0rd".toCharArray()); trustManagerFactory.init(keystore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); SSLContext context = SSLContext.getInstance("SSL"); context.init(null, trustManagers, null); SSLSocketFactory socketFact = context.getSocketFactory(); connection.setSSLSocketFactory(socketFact);
All of this works fine except in cases where users need other certificates or the default certificate.
I tried this Registering multiple keystores in JVM with no luck (I am having trouble generalizing it for my case)
How can I use my cert and still allow user libraries to use their own certs as well?
2 Answers
Answers 1
You are configuring a connection with a custom keystore acting as a truststore ( a certificate of your server that you trust). You are not overriding the default JVM behaviour, so the rest of the connection that other applications that include your library can make will not be affected.
Therefore you do not need a multiple keystore manager, in fact, your code works perfectly.
I've attached a full example below using a keystore google.jks
which includes Google's root CA, and a connection using the default JVM truststore. This is the output
request("https://www.google.com/", "test/google.jks", "pa55w0rd"); //OK request("https://www.aragon.es/", "test/google.jks", "pa55w0rd"); // FAIL sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target request("https://www.aragon.es/", null, null); //OK
The problem is not in the code you have attached, so check the following in your code:
The truststore
certs.keystore
is really found in your classpathTruststore settings are not set at JVM level using
-Djavax.net.ssl.trustStore
The errors found (please include it in your question) are really related to the SSL connection
package test; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.security.KeyStore; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; public class HTTPSCustomTruststore { public final static void main (String argv[]) throws Exception{ request("https://www.google.com/", "test/google.jks", "pa55w0rd"); //Expected OK request("https://www.aragon.es/","test/google.jks","pa55w0rd"); // Expected FAIL request("https://www.aragon.es/",null,null); //using default truststore. OK } public static void configureCustom(HttpsURLConnection connection, String truststore, String pwd)throws Exception{ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); KeyStore keystore = KeyStore.getInstance("JKS"); InputStream keystoreStream = HTTPSCustomTruststore.class.getClassLoader().getResourceAsStream(truststore); keystore.load(keystoreStream, pwd.toCharArray()); trustManagerFactory.init(keystore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); SSLContext context = SSLContext.getInstance("SSL"); context.init(null, trustManagers, new java.security.SecureRandom()); SSLSocketFactory socketFact = context.getSocketFactory(); connection.setSSLSocketFactory(socketFact); } public static void request(String urlS, String truststore, String pwd) { try { URL url = new URL(urlS); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); if (truststore != null) { configureCustom((HttpsURLConnection) conn, truststore, pwd); } conn.connect(); int statusCode = conn.getResponseCode(); if (statusCode != 200) { System.out.println(urlS + " FAIL"); } else { System.out.println(urlS + " OK"); } } catch (Exception e) { System.out.println(urlS + " FAIL " + e.getMessage()); } } }
Answers 2
You could import the default certificates into your custom store to have a combined custom store and use that.
0 comments:
Post a Comment