【发布时间】:2026-01-21 17:00:01
【问题描述】:
我有一个在树莓派上运行的简单 Web api,它位于同一个 pi 上的 nginx 服务器后面。我正在使用自签名客户端证书来验证来自 android 应用程序的调用。这在过去完全正常,但我最近在重建一些硬件后回到了这个项目,当我尝试在运行 Android 8.1 的 Pixel 2 上使用它时,它给出了以下异常:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:219)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:268)
...
我根据:http://nategood.com/client-side-certificate-authentication-in-ngi生成证书和密钥
使用curl 进行测试可以正常工作。
我为应用创建了密钥库以用于:
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
我按照以下文章在 Android 中设置客户端证书并连接到服务器:http://chariotsolutions.com/blog/post/https-with-client-certificates-on/
但我在 Kotlin 中使用 OkHttp 重写了它:
private const val SERVER = "https://my.server"
/**
* trustManagers is used to authorize the server's self-signed cert
*/
private val trustManagers by lazy {
val cert = CertificateFactory.getInstance("X.509")
.generateCertificate(appCtx.assets.open("ca.crt")) as X509Certificate
val trustStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
load(null, null)
setCertificateEntry(cert.subjectX500Principal.name, cert)
}
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply {
init(trustStore)
}.trustManagers
}
/**
* keyManagers is used to load the client-authentication cert
*/
private val keyManagers by lazy {
// assuming this can only be called after Application is created
val keyStore = KeyStore.getInstance("PKCS12").apply {
load(appCtx.assets.open("client.p12"), "".toCharArray())
}
KeyManagerFactory.getInstance("X509").apply {
init(keyStore, "".toCharArray())
}.keyManagers
}
/**
* sslContext for opening TLS connection to server
*/
private val sslContext by lazy {
SSLContext.getInstance("TLS").apply {
init(keyManagers, trustManagers, null)
}
}
/**
* pass an HTTPS request to server
*/
suspend fun request(url: String): ByteArray? {
return try {
val request = Request.Builder()
.url(url)
.build()
val client = OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustManagers[0] as X509TrustManager)
.build()
client.newCall(request).await().body().byteStream().readBytes()
} catch (e: Exception) {
err(e) { "failed to send request" }
null
}
}
这个使用可以工作,但现在不行了。我花了一天半的时间寻找答案,并尝试了以下方法:
- 我试过用 HttpURLConnection 代替 OkHttp
- 我已尝试从头开始重新创建所有证书/密钥。
- 我已尝试使用新的“网络安全配置”:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<!-- Additionally trust user added CAs -->
<certificates src="user" />
<certificates src="@raw/ca"/>
</trust-anchors>
</base-config>
</network-security-config>
我已经阅读了所有我能找到的关于创建自定义信任管理器的示例,它们几乎都是一样的,即使是 https://developer.android.com/training/articles/security-ssl.html#UnknownCa
我尝试过的所有方法都会产生相同的异常,我是否遗漏了什么?
【问题讨论】:
-
请注意,您需要在清单中注册该网络安全配置 XML。 FWIW,this is what I used for testing self-signed certs with network security configuration.
-
我确实包含在清单中,但我想我把它排除在我的问题之外。
-
嗨 bj0 我也面临同样的问题。有什么解决办法吗?
-
我无法让它与 CA 一起使用,但作为一种解决方法,您可以直接使用服务器证书并且它可以工作。
标签: android ssl openssl client-certificates