Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Registering multiple keystores in JVM

I have two applications running in the same java virtual machine, and both use different keystores and truststores.

A viable option would be use a single keystore and import all the other ones into the shared keystore (e.g. keytool -import), but it would really help my requirements if I could use separate keystores for separate applications running in the same jvm.

I could set the keystore and truststores to be used as jvm parameters or system properties as follows:

java -Djavax.net.ssl.keyStore=serverKeys  -Djavax.net.ssl.keyStorePassword=password  -Djavax.net.ssl.trustStore=serverTrust  -Djavax.net.ssl.trustStorePassword=password SSLApplication 

or

System.setProperty("javax.net.ssl.keyStore","serverKeys") 

But the problem with this approach is that it specifies the keystore/truststore to be used at a JVM level, thus all applications running in the same JVM gets the same keystore/truststore.

I have also tried creating a custom SSLContext and setting it as the default, but it also sets the context for all applications running in the same JVM.

SSLContext context = SSLContext.getInstance("SSL"); context.init(kms, tms, null); SSLContext.setDefault(context); 

I want to be able use different keystores/truststores without modifying individual application codes.

A solution that can dynamically register multiple key stores in addition to the default keystore/certs in jre into jvm would be great.

The solution will work in this way:

  • When JVM boots, it loads all the default certs/keystores from jre/certs folder (default java behaviour when no keystores specified).
  • When App 1 loads it registers its keystores,
  • then when App 2 loads it registers its keystores...

Please let me know your ideas or solutions. Thanks in advance!

like image 507
Raz Avatar asked Nov 25 '09 00:11

Raz


People also ask

Can you have multiple Keystores?

Correct, you can only have one “Keystore File Item”, but you can upload additional keystore files as regular files (Generic File Storage docs), and then just specify the passwords etc as App Env Vars or Secret Env Vars, or directly as the step(s)' input.

Where are Keystores stored?

The default location is /Users/<username>/. android/debug. keystore.

Is cacerts a keystore or TrustStore?

'cacerts' is a truststore. A trust store is used to authenticate peers. A keystore is used to authenticate yourself.

What is the difference between keystore and TrustStore?

TrustStore is used to store certificates from Certified Authorities (CA) that verify the certificate presented by the server in an SSL connection. While Keystore is used to store private key and identity certificates that a specific program should present to both parties (server or client) for verification.


1 Answers

Raz's answer was a great start, but wasn't quite flexible enough to meet my needs. The MultiStoreKeyManager explicitly checks the custom KeyManager and then falls back to the jvm KeyManager if an operation fails. I actually want to check jvm certs first; the best solution should be able to handle either case. Additionally, the answer fails to provide a working TrustManager.

I've written a couple more flexible classes, CompositeX509KeyManager and CompositeX509TrustManager, which add support for any number of keystores in an arbitrary order.

CompositeX509KeyManager

package com.mycompany.ssl;  import java.net.Socket; import java.security.Principal; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.List;  import javax.annotation.Nullable; import javax.net.ssl.X509KeyManager;  import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables;  /**  * Represents an ordered list of {@link X509KeyManager}s with most-preferred managers first.  *  * This is necessary because of the fine-print on {@link SSLContext#init}:  *     Only the first instance of a particular key and/or trust manager implementation type in the  *     array is used. (For example, only the first javax.net.ssl.X509KeyManager in the array will be used.)  *  * @author codyaray  * @since 4/22/2013  * @see http://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm  */ public class CompositeX509KeyManager implements X509KeyManager {    private final List keyManagers;    /**    * Creates a new {@link CompositeX509KeyManager}.    *    * @param keyManagers the X509 key managers, ordered with the most-preferred managers first.    */   public CompositeX509KeyManager(List keyManagers) {     this.keyManagers = ImmutableList.copyOf(keyManagers);   }    /**    * Chooses the first non-null client alias returned from the delegate    * {@link X509TrustManagers}, or {@code null} if there are no matches.    */   @Override   public @Nullable String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {     for (X509KeyManager keyManager : keyManagers) {       String alias = keyManager.chooseClientAlias(keyType, issuers, socket);       if (alias != null) {         return alias;       }     }     return null;   }    /**    * Chooses the first non-null server alias returned from the delegate    * {@link X509TrustManagers}, or {@code null} if there are no matches.    */   @Override   public @Nullable String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {     for (X509KeyManager keyManager : keyManagers) {       String alias = keyManager.chooseServerAlias(keyType, issuers, socket);       if (alias != null) {         return alias;       }     }     return null;   }    /**    * Returns the first non-null private key associated with the    * given alias, or {@code null} if the alias can't be found.    */   @Override   public @Nullable PrivateKey getPrivateKey(String alias) {     for (X509KeyManager keyManager : keyManagers) {       PrivateKey privateKey = keyManager.getPrivateKey(alias);       if (privateKey != null) {         return privateKey;       }     }     return null;   }    /**    * Returns the first non-null certificate chain associated with the    * given alias, or {@code null} if the alias can't be found.    */   @Override   public @Nullable X509Certificate[] getCertificateChain(String alias) {     for (X509KeyManager keyManager : keyManagers) {       X509Certificate[] chain = keyManager.getCertificateChain(alias);       if (chain != null && chain.length > 0) {         return chain;       }     }     return null;   }    /**    * Get all matching aliases for authenticating the client side of a    * secure socket, or {@code null} if there are no matches.    */   @Override   public @Nullable String[] getClientAliases(String keyType, Principal[] issuers) {     ImmutableList.Builder aliases = ImmutableList.builder();     for (X509KeyManager keyManager : keyManagers) {       aliases.add(keyManager.getClientAliases(keyType, issuers));     }     return emptyToNull(Iterables.toArray(aliases.build(), String.class));   }    /**    * Get all matching aliases for authenticating the server side of a    * secure socket, or {@code null} if there are no matches.    */   @Override   public @Nullable String[] getServerAliases(String keyType, Principal[] issuers) {     ImmutableList.Builder aliases = ImmutableList.builder();     for (X509KeyManager keyManager : keyManagers) {       aliases.add(keyManager.getServerAliases(keyType, issuers));     }     return emptyToNull(Iterables.toArray(aliases.build(), String.class));   }    @Nullable   private static <T> T[] emptyToNull(T[] arr) {     return (arr.length == 0) ? null : arr;   }  } 

CompositeX509TrustManager

package com.mycompany.ssl;  import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.List;  import javax.net.ssl.X509TrustManager;  import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables;  /**  * Represents an ordered list of {@link X509TrustManager}s with additive trust. If any one of the  * composed managers trusts a certificate chain, then it is trusted by the composite manager.  *  * This is necessary because of the fine-print on {@link SSLContext#init}:  *     Only the first instance of a particular key and/or trust manager implementation type in the  *     array is used. (For example, only the first javax.net.ssl.X509KeyManager in the array will be used.)  *  * @author codyaray  * @since 4/22/2013  * @see http://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm  */ public class CompositeX509TrustManager implements X509TrustManager {    private final List trustManagers;    public CompositeX509TrustManager(List trustManagers) {     this.trustManagers = ImmutableList.copyOf(trustManagers);   }    @Override   public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {     for (X509TrustManager trustManager : trustManagers) {       try {         trustManager.checkClientTrusted(chain, authType);         return; // someone trusts them. success!       } catch (CertificateException e) {         // maybe someone else will trust them       }     }     throw new CertificateException("None of the TrustManagers trust this certificate chain");   }    @Override   public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {     for (X509TrustManager trustManager : trustManagers) {       try {         trustManager.checkServerTrusted(chain, authType);         return; // someone trusts them. success!       } catch (CertificateException e) {         // maybe someone else will trust them       }     }     throw new CertificateException("None of the TrustManagers trust this certificate chain");   }    @Override   public X509Certificate[] getAcceptedIssuers() {     ImmutableList.Builder certificates = ImmutableList.builder();     for (X509TrustManager trustManager : trustManagers) {       certificates.add(trustManager.getAcceptedIssuers());     }     return Iterables.toArray(certificates.build(), X509Certificate.class);   }  } 

Usage

For the standard case of one keystore + jvm keystore, you can wire it up like this. I’m using Guava again, but in a Guicey wrapper this time:

@Provides @Singleton SSLContext provideSSLContext(KeyStore keystore, char[] password) {   String defaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm();   X509KeyManager customKeyManager = getKeyManager("SunX509", keystore, password);   X509KeyManager jvmKeyManager = getKeyManager(defaultAlgorithm, null, null);   X509TrustManager customTrustManager = getTrustManager("SunX509", keystore);   X509TrustManager jvmTrustManager = getTrustManager(defaultAlgorithm, null);    KeyManager[] keyManagers = { new CompositeX509KeyManager(ImmutableList.of(jvmKeyManager, customKeyManager)) };   TrustManager[] trustManagers = { new CompositeX509TrustManager(ImmutableList.of(jvmTrustManager, customTrustManager)) };    SSLContext context = SSLContext.getInstance("SSL");   context.init(keyManagers, trustManagers, null);   return context; }  private X509KeyManager getKeyManager(String algorithm, KeyStore keystore, char[] password) {   KeyManagerFactory factory = KeyManagerFactory.getInstance(algorithm);   factory.init(keystore, password);   return Iterables.getFirst(Iterables.filter(       Arrays.asList(factory.getKeyManagers()), X509KeyManager.class), null); }  private X509TrustManager getTrustManager(String algorithm, KeyStore keystore) {   TrustManagerFactory factory = TrustManagerFactory.getInstance(algorithm);   factory.init(keystore);   return Iterables.getFirst(Iterables.filter(       Arrays.asList(factory.getTrustManagers()), X509TrustManager.class), null);  } 

I extracted this from my blog post about this problem which has a bit more detail, motivation, etc. All the code is there though, so its standalone. :)

like image 87
Cody A. Ray Avatar answered Oct 08 '22 13:10

Cody A. Ray