本文介绍了带有自签名CA的OkHttpClient可以在JVM上运行,但在Android上失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我正在尝试与本地网络中的受SSL保护的服务器(https
)通信,该服务器使用自签名证书颁发机构来生成证书。我正在使用okhttp-tls
创建SSLSocketFactory
和TrustManager
(根据OkHttp配方:CustomTrust)。相关代码片段如下:
val certificateFactory = CertificateFactory.getInstance("X.509")
val certificate = certificateFactory.generateCertificate(caFileInputStream) as X509Certificate
val certificates = HandshakeCertificates.Builder()
.addTrustedCertificate(certificate)
.build()
val client = OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager)
.addInterceptor(loggingInterceptor)
.dns(dns)
.build()
我还使用自定义Dns
实现将域名从请求url解析为我们的自定义IP地址。
作为不可靠的测试,我从相同的okhttp示例中为我们的一个有效REST终结点调用showUrl
方法。
测试用例在JVM(单元测试)上运行成功,但在Android(AndroidTest)上运行失败,CertPathValidatorException: Trust anchor for certification path not found
以下是整个堆栈跟踪
2021-12-21 15:43:21.951 11338-11338/com.kshitijpatil.sslsandbox E/ShowURL: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.SSLUtils.toSSLHandshakeException(SSLUtils.java:363)
at com.android.org.conscrypt.ConscryptEngine.convertException(ConscryptEngine.java:1134)
at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1089)
at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876)
at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747)
at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712)
at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:858)
at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.access$100(ConscryptEngineSocket.java:731)
at com.android.org.conscrypt.ConscryptEngineSocket.doHandshake(ConscryptEngineSocket.java:241)
at com.android.org.conscrypt.ConscryptEngineSocket.startHandshake(ConscryptEngineSocket.java:220)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:379)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.kt:221)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
at com.kshitijpatil.sslsandbox.NetworkService.showUrl(NetworkService.kt:169)
at com.kshitijpatil.sslsandbox.MainActivity$onCreate$1$1$1$1.invokeSuspend(MainActivity.kt:24)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:39)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:672)
at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:549)
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:505)
2021-12-21 15:43:21.956 11338-11338/com.kshitijpatil.sslsandbox E/ShowURL: at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:425)
at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:353)
at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:94)
at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:90)
at com.android.org.conscrypt.ConscryptEngineSocket$2.checkServerTrusted(ConscryptEngineSocket.java:163)
at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:255)
at com.android.org.conscrypt.ConscryptEngine.verifyCertificateChain(ConscryptEngine.java:1638)
at com.android.org.conscrypt.NativeCrypto.ENGINE_SSL_read_direct(Native Method)
at com.android.org.conscrypt.NativeSsl.readDirectByteBuffer(NativeSsl.java:569)
at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataDirect(ConscryptEngine.java:1095)
at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1079)
... 36 more
Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
... 50 more
我还在使用以下网络安全配置,并已在AndroidManifest.xml
<?xml version="1.0" encoding="UTF-8" ?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="user"/>
<certificates src="system"/>
<certificates src="@raw/<custom_ca_path>" />
</trust-anchors>
</base-config>
</network-security-config>
我认为这个问题与OkHttp的某些平台特定的SocketFactory
实现有关,因为单元测试通过了,但androidTest失败了。如有任何帮助,我们将不胜感激。
编辑:
编写如下自定义TrustManager以某种方式解决了该问题。我不确定这是不是一个足够好的解决方案。val certificate = certificateFactory.generateCertificate(caFileInputStream) as X509Certificate
val trustManager = object : X509TrustManager {
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
chain.forEach {
it.checkValidity()
try {
it.verify(certificate.publicKey)
} catch (e: Exception) {
e.printStackTrace()
throw CertificateException(e.message)
}
}
}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return emptyArray()
}
}
推荐答案
此示例显示它使用网络安全配置中的自定义CA或使用受信任证书配置的客户端。
握手证书:
val certificate = """
-----BEGIN CERTIFICATE-----
MIIBMTCB2KADAgECAgEBMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMMCWxvY2FsaG9z
dDAeFw0yMTEyMjMwNTQ5MTBaFw0yMTEyMjQwNTQ5MTBaMBQxEjAQBgNVBAMMCWxv
Y2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABORfR8tCugvrBb3aWRPY
NsNhEYWMhld41tDaQdUkIs8tKKdgdbJd1XSbITotW2WsgQLqkplZp1Eqabg6ezIn
ka2jGzAZMBcGA1UdEQEB/wQNMAuCCWxvY2FsaG9zdDAKBggqhkjOPQQDAgNIADBF
AiEA0Zw+tLJD017M1gM7dQeNolxk7YffV9LuizrUV9meUIMCIFNj64gbRDmK0Yps
qJH4bEVX0U8lbj1UD852nDGud7ZY
-----END CERTIFICATE-----
""".trimIndent()
val trustedCert = certificate.decodeCertificatePem()
val clientCertificates = HandshakeCertificates.Builder()
.addTrustedCertificate(trustedCert)
.build()
client = client.newBuilder()
.sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager)
.build()
这篇关于带有自签名CA的OkHttpClient可以在JVM上运行,但在Android上失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!