V2EX 10月10日 16:38
安卓 HTTPS 动态证书配置与抓包防护
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

该内容介绍了如何在安卓应用中动态配置 HTTPS 证书以防止抓包。文章指出,静态配置证书虽然能实现 HTTPS,但证书过期后会导致网络认证失败。为了解决这个问题,作者分享了一段 OkHttp 的代码,实现了动态 SSL 证书校验。该方案会先判断证书是否有效,如果有效则走证书认证;如果无效或找不到证书,则直接走信任证书,从而确保应用在证书过期后仍能正常运行,并有效防止抓包。

🔒 **动态 SSL 配置以应对证书过期问题**:文章的核心在于解决了静态配置 HTTPS 证书时,证书过期会导致应用无法联网的痛点。通过动态配置 SSL 证书,可以先判断证书的有效性,若有效则进行严格的证书验证,若无效或找不到证书,则自动切换到信任模式,确保了应用在证书过期后仍能正常运行,避免了因证书过期导致的服务中断。

🛡️ **强大的抓包防护机制**:通过代码层面的动态 SSL 配置,应用能够有效地抵御中间人攻击和抓包行为。该方案通过自定义 `TrustManager`,在验证服务器证书时,不仅检查其是否过期,还通过比较证书的公钥指纹来进一步确认证书的真实性,防止伪造证书的出现,从而保障了通信的安全性。

🔧 **代码实现与 OkHttp 集成**:文章提供了具体的 Kotlin 代码实现,展示了如何使用 OkHttp 的 `Builder` 配置动态 SSL。代码中包含了 `SSLConfigUtil` 工具类和 `DynamicSSLTrustManager`,前者负责 OkHttp 的配置,后者则实现了动态证书的加载、校验逻辑以及异常处理,使得开发者能够方便地将此安全机制集成到现有项目中。

🔄 **灵活的证书管理**:该方案支持动态添加证书,无论是从文件还是字节数组,都可以方便地注入新的证书。同时,也提供了设置和检查“跳过校验”模式的接口,为特殊场景(如测试环境)提供了灵活性,但同时也强调了在生产环境中应谨慎使用此功能,以保证最高的安全性。

帮公司的安卓小哥来提个问题:之前项目是没有配置证书的,可以被抓包,现在为了防止抓包,就需要配置 https 证书。首先静态配置证书是属于写到 network_security_config_app.xml 这个证书,直接配置到网络认证文件里,这个种情况有个不好就是如果哪天证书过期了,那就无法通过网络认证了,但是不希望如此,希望如果过期了那就忽略认证,所以需要动态配置 ssl 不直接配置到文件中,通过代码去认证,先去判断证书有效,如果无效或者不能找到证书直接走信任证书,如果有效那就走证书认证,目前没配置的表现就是可以被抓包。请问大家,有过这方面的经验吗? GPT 代码帮忙解决了,但是因为是知识盲区想请教一下诸位。package com.wlld.common.network

import android.content.Contextimport android.util.Logimport com.wlld.common.utils.LogUtilsimport okhttp3.OkHttpClientimport javax.net.ssl.SSLContextimport javax.net.ssl.TrustManagerimport javax.net.ssl.X509TrustManagerimport java.security.SecureRandom

/**

package com.wlld.common.network

import android.content.Contextimport android.util.Logimport com.wlld.common.Rimport java.io.ByteArrayInputStreamimport java.io.InputStreamimport java.security.KeyStoreimport java.security.KeyStoreExceptionimport java.security.NoSuchAlgorithmExceptionimport java.security.PrivateKeyimport java.security.cert.Certificateimport java.security.cert.CertificateExceptionimport java.security.cert.CertificateFactoryimport java.security.cert.X509Certificateimport java.util.Dateimport javax.net.ssl.TrustManagerFactoryimport javax.net.ssl.X509TrustManager

/**

// // 如果自定义证书有效,优先使用// for (customCert in customCertificates) {// if (!isCertificateExpired(customCert)) {// // 检查自定义证书是否覆盖 weilaiqiyuan.com 域名// val subjectCN = extractCommonName(customCert.subjectDN.toString())// if (subjectCN.contains("*.weilaiqiyuan.com") || subjectCN.contains("weilaiqiyuan.com")) {// Log.d(TAG, "使用有效的自定义证书验证 weilaiqiyuan.com 域名: $subjectCN")// return true// }// }// }

    // 检查链中的证书是否与自定义证书匹配    for (cert in chain) {        for (customCert in customCertificates) {            if (cert.subjectDN == customCert.subjectDN) {                // 找到匹配的自定义证书                if (!isCertificateExpired(customCert)) {                    // 🔍 严格验证证书:比较公钥指纹,防止证书伪造                    if (verifyCertificateFingerprint(cert, customCert)) {                        Log.d(TAG, "✅ 证书验证通过(指纹匹配): ${cert.subjectDN}")                        return true                    } else {                        Log.w(TAG, "❌ 证书验证失败(指纹不匹配)- 可能是抓包攻击!")                        // 指纹不匹配,拒绝连接                        throw CertificateException("证书指纹验证失败,可能存在中间人攻击")                    }                } else {                    Log.w(TAG, "自定义证书已过期: ${cert.subjectDN}")                    return false                }            }        }    }        return false}/** * 验证证书指纹,防止证书伪造 */private fun verifyCertificateFingerprint(cert1: X509Certificate, cert2: X509Certificate): Boolean {    return try {        // 比较公钥的 SHA-256 指纹        val pubkey1 = cert1.publicKey.encoded        val pubkey2 = cert2.publicKey.encoded                val digest1 = java.security.MessageDigest.getInstance("SHA-256").digest(pubkey1)        val digest2 = java.security.MessageDigest.getInstance("SHA-256").digest(pubkey2)                val fingerprint1 = digest1.joinToString("") { "%02X".format(it) }        val fingerprint2 = digest2.joinToString("") { "%02X".format(it) }                val isValid = fingerprint1 == fingerprint2        Log.d(TAG, "证书指纹对比: $fingerprint1 vs $fingerprint2, 匹配: $isValid")        isValid    } catch (e: Exception) {        Log.e(TAG, "证书指纹验证失败", e)        false    }}/** * 从 SubjectDN 中提取 Common Name */private fun extractCommonName(subjectDN: String): String {    val cnPattern = "CN=([^,]+)".toRegex()    val match = cnPattern.find(subjectDN)    return match?.groupValues?.get(1) ?: ""}/** * 获取自定义证书信息 */fun getCertificateInfo(): List<String> {    return customCertificates.map { cert ->        "Subject: ${cert.subjectDN}, Issuer: ${cert.issuerDN}, Expires: ${cert.notAfter}, Expired: ${isCertificateExpired(cert)}"    }}

}

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Android HTTPS SSL 证书 抓包防护 OkHttp 动态配置 网络安全 代码 TrustManager
相关文章