Skip to content

Instantly share code, notes, and snippets.

@yaroslav-shlapak
Last active November 19, 2025 16:36
Show Gist options
  • Select an option

  • Save yaroslav-shlapak/046f3e100372452f34cc19453848f0d3 to your computer and use it in GitHub Desktop.

Select an option

Save yaroslav-shlapak/046f3e100372452f34cc19453848f0d3 to your computer and use it in GitHub Desktop.
How to use certificates installed by user to Android system with OkHttp
import java.security.KeyStore
import java.security.KeyStoreException
import java.security.NoSuchAlgorithmException
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
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 [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 [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.)
*
*/
class CompositeX509TrustManager : X509TrustManager {
private val trustManagers: List<X509TrustManager?>
constructor(trustManagers: List<X509TrustManager?>) {
this.trustManagers = trustManagers.toList()
}
constructor(keystore: KeyStore?) {
this.trustManagers = listOf(defaultTrustManager, getTrustManager(keystore))
}
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
for (trustManager in trustManagers) {
try {
trustManager?.checkClientTrusted(chain, authType)
return // someone trusts them. success!
} catch (e: CertificateException) {
// maybe someone else will trust them
}
}
throw CertificateException("None of the TrustManagers trust this certificate chain")
}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
for (trustManager in trustManagers) {
try {
trustManager?.checkServerTrusted(chain, authType)
return // someone trusts them. success!
} catch (e: CertificateException) {
// maybe someone else will trust them
}
}
throw CertificateException("None of the TrustManagers trust this certificate chain")
}
override fun getAcceptedIssuers(): Array<X509Certificate> {
val certificates = mutableListOf<X509Certificate>()
for (trustManager in trustManagers) {
if (trustManager != null) {
for (cert in trustManager.acceptedIssuers) {
certificates.add(cert)
}
}
}
return certificates.toTypedArray()
}
companion object {
fun getTrustManagers(keyStore: KeyStore?): Array<TrustManager> {
return arrayOf(CompositeX509TrustManager(keyStore))
}
val defaultTrustManager: X509TrustManager?
get() = getTrustManager(null)
fun getTrustManager(keystore: KeyStore?): X509TrustManager? {
return getTrustManager(TrustManagerFactory.getDefaultAlgorithm(), keystore)
}
fun getTrustManager(algorithm: String, keystore: KeyStore?): X509TrustManager? {
val factory: TrustManagerFactory
try {
factory = TrustManagerFactory.getInstance(algorithm)
factory.init(keystore)
return factory.trustManagers.first { it is X509TrustManager? } as X509TrustManager?
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: KeyStoreException) {
e.printStackTrace()
}
return null
}
}
val ALLOW_SSL_ERRORS_CERTIFICATES_FOR_HTTP_CLIENT = SslErrorsStateForHttpClient.ALLOW_DEFAULT_AND_SYSTEM_CA
enum class SslErrorsStateForHttpClient {
ALLOW_ALL,
ALLOW_NOTHING,
ALLOW_DEFAULT_AND_CUSTOM,
ALLOW_DEFAULT_AND_SYSTEM_CA
}
import okhttp3.OkHttpClient
import timber.log.Timber
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.security.KeyManagementException
import java.security.KeyStore
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.util.*
import javax.net.ssl.*
/**
* Created on 2019/06/26.
*/
object SSLConfigFactory {
const val TAG = "SSLConfigFactory"
fun provideSSLContext(keystore: KeyStore, password: CharArray): SSLContext {
val defaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm()
val customKeyManager = getKeyManager("SunX509", keystore, password)
val jvmKeyManager = getKeyManager(defaultAlgorithm, null, null)
val customTrustManager = getTrustManager("SunX509", keystore)
val jvmTrustManager = getTrustManager(defaultAlgorithm, null)
val keyManagers = arrayOf<KeyManager>(
CompositeX509KeyManager(listOf(jvmKeyManager, customKeyManager))
)
val trustManagers = arrayOf<TrustManager>(
CompositeX509TrustManager(listOf(jvmTrustManager, customTrustManager))
)
val context = SSLContext.getInstance("SSL")
context.init(keyManagers, trustManagers, null)
return context
}
private fun getKeyManager(algorithm: String, keystore: KeyStore?, password: CharArray?): X509KeyManager? {
val factory = KeyManagerFactory.getInstance(algorithm)
factory.init(keystore, password)
return factory.keyManagers.first { it is X509KeyManager? } as X509KeyManager?
}
fun trustDefaultAndCustomCertificates(httpClient: OkHttpClient.Builder) {
try {
val cf: CertificateFactory = CertificateFactory.getInstance("X.509")
val cert =
"-----BEGIN CERTIFICATE-----\n" +
"MIIEbTCCAlWgAwIBAgIUBmEymduV1Jbp8t97Lf9kDoFFN+EwDQYJKoZIhvcNAQEL\n" +
"BQAwNDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRgwFgYDVQQKDA9NZXRyaWNJ\n" +
"bnNpZ2h0cy4wHhcNMTkwNjI1MTUxMzIyWhcNMjIwNDE0MTUxMzIyWjBcMQswCQYD\n" +
"VQQGEwJVUzELMAkGA1UECAwCQ0ExGDAWBgNVBAoMD01ldHJpY0luc2lnaHRzLjEm\n" +
"MCQGA1UEAwwdc2VsZnNpZ25lZC5tZXRyaWNpbnNpZ2h0cy5jb20wggEiMA0GCSqG\n" +
"SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG/HCCj/TZW74nsaUVXs/qWZWCvK5MKz3d\n" +
"5xlwpL0r5vdCkBQIv4PVHdN5WD1J8+esdQK0XvemCPgA0wUTC8N3zGcVeQo8yZPz\n" +
"K6jxbQKceduFSWvCYx0sqO3xh/VhWOI7lYmJvFmMguMPl+pC8YNFAwIr/O10mkif\n" +
"CLDC8YlssanbVWXzi1HtobmPpIUEyONT1LaSqW9AOho0W99GdwBNbsx/Dy92zoFk\n" +
"4PTsvfEbl6o/PpGfXMdoEK3ZMtrr9D3DRAss/d+KDgxFYl5hfSM/7AOXQhUj6JCk\n" +
"D6w94m298c19Q+NlZTPhoxOicO2YuL4Xf+RUDvJZcocM+pXKOy6vAgMBAAGjTzBN\n" +
"MEsGA1UdEQREMEKCHXNlbGZzaWduZWQubWV0cmljaW5zaWdodHMuY29tgiF3d3cu\n" +
"c2VsZnNpZ25lZC5tZXRyaWNpbnNpZ2h0cy5jb20wDQYJKoZIhvcNAQELBQADggIB\n" +
"ABbrIz2HXi5Pux+T8hrrIY0VZckcQ8c2u1/VK+Tqd8UayydkRlgfhEWWrr8gCJ+s\n" +
"uji4jBXHBUMm7Qk4O1VjfgasE1ojKWKoWKnDiaWcJ7d8WLPEh+EO27URklqvcZUy\n" +
"HqnukJMEFImn9RZqo+RcQ5huT2KcUhtzYxLhpuFh4lKv9tId7+3rQgAAykdHMZPf\n" +
"fLfkiQbg6rEUJtAkN6kl67qECFRUhrD2Z8Osn1OkIYgq0yEs6Vc3Bc2GbKRejtrQ\n" +
"M0jpApXCg6XUioPacdclzq2wtWCyh+CCBDnZr5jjYABN4N3X8s6HORkU7wSx2NFv\n" +
"UApizWmE+xxzCnz3JMBeMiA58z9lYQtF1c8dac6A2gdlqA5I8eLQo+0YgLo7rkXG\n" +
"CE5IlFxFLg3h+yCxXNge21TPALOmb6LrEjRfNCHa6CgP6ydkElVmAhEE9uhDPZFQ\n" +
"XnPeaRPG0dhqORDzx1NLIzxhYaiGatKGPv6QYTGExTHSbRj5pbjVETJI17DWmsQx\n" +
"sl4FCWUZmwKNGfpLLsdBFpgsSgb9MBygqhQP+P8Wn1ZBDAezTePPLwpE0hhdCcR5\n" +
"OWiknKDgdtHPzgI0xAtIxK5VdjSEueJIJxpjF85/n03b+iu7bHQmnZBOohun50rO\n" +
"XPDaCT3nAWRvph7U+FwJG8b5ZNXt76FQO+ORIfRIQ8N0\n" +
"-----END CERTIFICATE-----"
val caInput: InputStream = ByteArrayInputStream(cert.toByteArray(Charsets.UTF_8))
val ca: X509Certificate = caInput.use {
cf.generateCertificate(it) as X509Certificate
}
// Create a KeyStore containing our trusted CAs
val keyStoreType = KeyStore.getDefaultType()
val keyStore = KeyStore.getInstance(keyStoreType).apply {
load(null, null)
setCertificateEntry("ca", ca)
}
val sslContext = SSLContext.getInstance("TLS")
val tm = CompositeX509TrustManager.getTrustManagers(keyStore)
sslContext.init(null, tm, null)
httpClient
.sslSocketFactory(sslContext.socketFactory)
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: KeyManagementException) {
e.printStackTrace()
}
}
fun trustDefaultAndSystemCertificates(httpClient: OkHttpClient.Builder) {
try {
val keyStore: KeyStore? = KeyStore.getInstance("AndroidCAStore").apply {
load(null, null)
}
val sslContext = SSLContext.getInstance("TLS")
val tm = CompositeX509TrustManager.getTrustManagers(keyStore)
sslContext.init(null, tm, null)
httpClient
.sslSocketFactory(sslContext.socketFactory)
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: KeyManagementException) {
e.printStackTrace()
}
}
fun trustAllCertificates(endpoint: String, httpClient: OkHttpClient.Builder) {
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate> {
return emptyArray()
}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
}
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
}
})
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
val hostnameVerifier = object : HostnameVerifier {
override fun verify(hostname: String, session: SSLSession): Boolean {
Timber.tag(TAG).i("input host: $hostname, endpoint: $endpoint")
return endpoint.toLowerCase(Locale.getDefault())
.contains(hostname.toLowerCase(Locale.getDefault()))
}
}
httpClient
.sslSocketFactory(sslContext.socketFactory)
.hostnameVerifier(hostnameVerifier)
}
fun printInstalledCertificates() {
try {
val ks = KeyStore.getInstance("AndroidCAStore")
if (ks != null) {
ks.load(null, null)
val aliases = ks.aliases()
while (aliases.hasMoreElements()) {
val alias = aliases.nextElement()
val cert = ks.getCertificate(alias) as X509Certificate
//To print System Certs only
if (cert.issuerDN.name.contains("system")) {
Timber.d("system: ${cert.issuerDN.name}")
} else
//To print User Certs only
if (cert.issuerDN.name.contains("user")) {
Timber.d("user: ${cert.issuerDN.name}")
} else {
Timber.d("other: ${cert.issuerDN.name}")
}
//To print all certs
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun applySslHandlingStrategy(endpoint: String, httpClient: OkHttpClient.Builder) {
when (ALLOW_SSL_ERRORS_CERTIFICATES_FOR_HTTP_CLIENT) {
SslErrorsStateForHttpClient.ALLOW_ALL -> {
SSLConfigFactory.trustAllCertificates(endpoint, httpClient)
}
SslErrorsStateForHttpClient.ALLOW_DEFAULT_AND_CUSTOM -> {
SSLConfigFactory.trustDefaultAndCustomCertificates(httpClient)
}
SslErrorsStateForHttpClient.ALLOW_DEFAULT_AND_SYSTEM_CA -> {
SSLConfigFactory.trustDefaultAndSystemCertificates(httpClient)
}
SslErrorsStateForHttpClient.ALLOW_NOTHING -> {
// do nothing
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment