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:
Please let me know your ideas or solutions. Thanks in advance!
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.
The default location is /Users/<username>/. android/debug. keystore.
'cacerts' is a truststore. A trust store is used to authenticate peers. A keystore is used to authenticate yourself.
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.
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.
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; } }
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); } }
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. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With