如何用KeyChain的X509Certificate替代KeyStore配置OkHttpClient?
用KeyChain证书替换KeyStore配置OkHttpClient的方案
核心逻辑
- 把KeyChain拿到的证书转成可信证书集合,代替原来从assets加载到KeyStore的证书
- 自定义
X509TrustManager时,得把证书验证逻辑写对,不能只做空实现 - 给OkHttpClient配置SSL上下文时,要把自定义TrustManager和SocketFactory绑定好
具体代码实现
1. 从KeyChain取证书链
先确保你已经通过KeyChain.choosePrivateKeyAlias拿到了证书的别名,然后直接取证书链:
// 假设已经拿到证书别名certAlias val certChain = KeyChain.getCertificateChain(context, certAlias) // 过滤出X509证书(你已经能正确获取,直接用就行) val trustedCertificates = certChain.filterIsInstance<X509Certificate>()
2. 写靠谱的自定义TrustManager
之前自定义无效大概率是没正确实现验证逻辑,这里给你一个能正常工作的版本:
class KeyChainTrustManager(private val trustedCerts: List<X509Certificate>) : X509TrustManager { override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) { // 如果不需要客户端认证,直接抛异常就行,或者根据你自己的需求改 throw IllegalArgumentException("Client auth not supported") } override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) { chain ?: throw IllegalArgumentException("Server cert chain can't be null") // 先检查服务器证书是否在我们的可信列表里 val serverCert = chain[0] val isTrusted = trustedCerts.any { trustedCert -> trustedCert.publicKey == serverCert.publicKey && trustedCert.subjectDN.name == serverCert.subjectDN.name } if (!isTrusted) { throw CertificateException("Server cert not trusted") } // 可选:验证证书链的完整性(如果你的证书有中间证书的话) val trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) trustFactory.init(KeyStore.getInstance(KeyStore.getDefaultType())) val defaultTrustManager = trustFactory.trustManagers.first() as X509TrustManager try { defaultTrustManager.checkServerTrusted(chain, authType) } catch (e: CertificateException) { throw CertificateException("Server cert chain validation failed", e) } } override fun getAcceptedIssuers(): Array<X509Certificate> { return trustedCerts.toTypedArray() } }
3. 配置OkHttpClient
把上面的TrustManager关联到SSL上下文,再给OkHttpClient用上:
fun buildOkHttpClient(context: Context, certAlias: String): OkHttpClient { val certChain = KeyChain.getCertificateChain(context, certAlias) val trustedCerts = certChain.filterIsInstance<X509Certificate>() val trustManager = KeyChainTrustManager(trustedCerts) // 初始化SSL上下文 val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, arrayOf(trustManager), SecureRandom()) return OkHttpClient.Builder() .sslSocketFactory(sslContext.socketFactory, trustManager) // 如果证书的CN/SAN和请求域名不匹配,临时加这行(生产环境别随便用) // .hostnameVerifier { _, _ -> true } .build() }
为啥之前自定义TrustManager没用?你可以排查这几点
- 验证逻辑写得不对:只空实现了方法,没实际检查服务器证书是否在可信列表里
- SSLContext没绑定对:初始化SSLContext时没传入自定义的TrustManager,等于白写
- 主机名验证失败:证书的域名字段和你请求的域名不匹配,哪怕证书可信也会报错
- 证书链不全:
KeyChain.getCertificateChain返回的证书链缺了中间或根证书,导致验证失败
内容的提问来源于stack exchange,提问作者Mr Heelis
相关产品推荐
相关产品推荐

