Wednesday, May 30, 2018

How can my library with built in ssl certificate also allow use with default certs

Leave a Comment

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 classpath

  • Truststore 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.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment