Android端程序化重建证书链解决服务器证书不完整问题
程序化修复Android端*.nkust.edu.tw证书链不完整问题
问题背景
我的Android应用需要登录并爬取*.nkust.edu.tw域名下的数据,但该服务器证书链安装不完整,导致使用Kotlin+Ktor发起网络请求时,下载PDF等操作会抛出sun.security.provider.certpath.SunCertPathBuilderException异常。由于无法修改服务器配置,需通过程序化方式重建证书链来解决问题。此前参考过将单个证书添加到信任管理器的方案,但不适用于证书链不完整的场景。
解决方案步骤
1. 生成完整证书链文件
获取该域名对应的服务器证书、中间UCA证书、根证书,将三个证书按「服务器证书 → 中间UCA证书 → 根证书」的顺序合并为一个PEM格式的证书文件(后缀为.pem),并将文件放置在Android项目的res/raw目录下。
2. 在Ktor中配置自定义SSL信任管理器
通过自定义TrustManager加载完整证书链,替换Ktor默认的SSL验证逻辑,具体实现代码如下:
import io.ktor.client.HttpClient import io.ktor.client.engine.android.Android import java.io.InputStream import java.security.KeyStore import java.security.cert.CertificateFactory import javax.net.ssl.SSLContext import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager fun createCustomHttpClient(context: Context): HttpClient { // 读取raw目录下的完整证书链文件 val certInputStream: InputStream = context.resources.openRawResource(R.raw.full_chain) // 初始化证书工厂和密钥库 val certificateFactory = CertificateFactory.getInstance("X.509") val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply { load(null) // 将证书链中的所有证书加载到密钥库 val certificates = certificateFactory.generateCertificates(certInputStream) certificates.forEachIndexed { index, cert -> setCertificateEntry("cert_$index", cert) } } // 构建自定义TrustManager val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply { init(keyStore) } val trustManagers = trustManagerFactory.trustManagers val x509TrustManager = trustManagers.first { it is X509TrustManager } as X509TrustManager // 创建SSLContext并设置自定义TrustManager val sslContext = SSLContext.getInstance("TLS").apply { init(null, arrayOf(x509TrustManager), java.security.SecureRandom()) } // 配置Ktor HttpClient return HttpClient(Android) { engine { sslContext = sslContext } } }
注意事项
- 务必确保获取的证书来源可靠,避免加载恶意证书导致中间人攻击风险。
- 证书链的顺序不能颠倒,必须以服务器证书开头,依次到中间证书、根证书,否则证书验证依然会失败。
内容的提问来源于stack exchange,提问作者lrnbrad
相关产品推荐
相关产品推荐

