Skip to content

Instantly share code, notes, and snippets.

@HughJeffner
Last active June 9, 2022 21:01
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save HughJeffner/6eac419b18c6001aeadb to your computer and use it in GitHub Desktop.
Save HughJeffner/6eac419b18c6001aeadb to your computer and use it in GitHub Desktop.
Trust both system KeyStore and custom KeyStore on Android
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* 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 <a href="http://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm">
* http://stackoverflow.com/questions/1793979/registering-multiple-keystores-in-jvm
* </a>
*/
@SuppressWarnings("unused")
public class CompositeX509TrustManager implements X509TrustManager {
private final List<X509TrustManager> trustManagers;
public CompositeX509TrustManager(List<X509TrustManager> trustManagers) {
this.trustManagers = ImmutableList.copyOf(trustManagers);
}
public CompositeX509TrustManager(KeyStore keystore) {
this.trustManagers = ImmutableList.of(getDefaultTrustManager(), getTrustManager(keystore));
}
@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<X509Certificate> certificates = ImmutableList.builder();
for (X509TrustManager trustManager : trustManagers) {
for (X509Certificate cert : trustManager.getAcceptedIssuers()) {
certificates.add(cert);
}
}
return Iterables.toArray(certificates.build(), X509Certificate.class);
}
public static TrustManager[] getTrustManagers(KeyStore keyStore) {
return new TrustManager[] { new CompositeX509TrustManager(keyStore) };
}
public static X509TrustManager getDefaultTrustManager() {
return getTrustManager(null);
}
public static X509TrustManager getTrustManager(KeyStore keystore) {
return getTrustManager(TrustManagerFactory.getDefaultAlgorithm(), keystore);
}
public static X509TrustManager getTrustManager(String algorithm, KeyStore keystore) {
TrustManagerFactory factory;
try {
factory = TrustManagerFactory.getInstance(algorithm);
factory.init(keystore);
return Iterables.getFirst(Iterables.filter(
Arrays.asList(factory.getTrustManagers()), X509TrustManager.class), null);
} catch (NoSuchAlgorithmException | KeyStoreException e) {
e.printStackTrace();
}
return null;
}
}
try {
KeyStore keystore; // Get your own keystore here
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] tm = CompositeX509TrustManager.getTrustManagers(keystore);
sslContext.init(null, tm, null);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
@firegloves
Copy link

cool, really nice class, thx a lot :)

@ianlin
Copy link

ianlin commented Aug 17, 2016

Nice job!

@Inferno-88
Copy link

This one came in handy, thanks!

@daan-navara
Copy link

Thanks a lot!

@davidrauca93
Copy link

Thanks a lot!

@alvindizon
Copy link

Your code helped me a lot as well, thank you!

@protyay
Copy link

protyay commented Dec 4, 2019

Hey, thanks man. This helped a lot! :)

@Hakky54
Copy link

Hakky54 commented Jun 29, 2020

Thank you for sharing HughJeffner! After founding this gist I checked with the original author and discovered the snippet for CompositeX509KeyManager. I included these two classes within my library named GitHub - SSLContext-Kickstart. I thought it would be maybe handy to share it here

@sushantsj
Copy link

Hey, I tried executing this I found out an issue that we are adding an custom keystore1, but when we add another custom keystore2, the previous one gets deleted. Can you help?

@Hakky54
Copy link

Hakky54 commented May 12, 2021

What I think you should do is transform the custom keystore into a trustmanager and do that also to the other one. Add those multiple trustmanager into a list and supply it to the constructor of this CompositeTrustManager

@sushantsj
Copy link

sushantsj commented May 12, 2021 via email

@Hakky54
Copy link

Hakky54 commented May 12, 2021

I think the solution is pretty straight forward. The CompositeTrustManager is using an immutable list, but if you change that to an arraylist you will be able to adjust it during runtime.

So if you add the following snippet it should probably do the trick:

public void addTrustStore(KeyStore trustStore) {
    X509TrustManager trustManager = getTrustManager(trustStore);
    this.trustManagers.add(trustManager);
}

@sushantsj
Copy link

sushantsj commented May 12, 2021 via email

@ChauhanAndroid
Copy link

java.lang.IllegalStateException: Unable to extract the trust manager on AndroidPlatform, sslSocketFactory is class com.google.android.gms.org.conscrypt.OpenSSLSocketFactoryImpl
at okhttp3.OkHttpClient$Builder.sslSocketFactory(OkHttpClient.kt:751)

@reubenjackson
Copy link

Thank you so much. This solved my problem!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment