1. HTTPS基础与证书链验证
1.1 TLS握手协议流程
TLS(Transport Layer Security)握手是HTTPS建立安全通信通道的核心过程。客户端与服务端通过四次握手交换密钥材料,最终协商出会话密钥用于对称加密。握手阶段包含证书链下发与验证。
- ClientHello:客户端发送支持的TLS版本、密码套件列表、随机数。
- ServerHello:服务端选定TLS版本、密码套件,发送服务端随机数。
- Certificate:服务端发送证书链(从服务器证书到根证书)。
- ServerKeyExchange / CertificateRequest(可选):密钥协商或请求客户端证书。
- ServerHelloDone:服务端完成握手消息发送。
- ClientKeyExchange:客户端发送预主密钥(用服务端公钥加密)。
- ChangeCipherSpec:双方切换为对称加密。
- Finished:验证握手消息完整性。
证书验证时机:客户端收到
Certificate消息后,立即对证书链执行完整性、有效期、吊销状态、域名匹配和签名验证。只有验证通过才能继续后续握手。
1.2 证书链结构与CA信任机制
数字证书遵循X.509 v3标准。一个完整的证书链通常由三级组成:
| 层级 | 角色 | 颁发者 | 信任锚 |
|---|---|---|---|
| 叶子证书 | 服务器证书 | 中间CA | 非自签 |
| 中间CA证书 | 桥梁 | 根CA | 非自签 |
| 根CA证书 | 信任锚 | 自签名 | 预置于信任存储 |
证书链验证遵循信任锚模型:从叶子向上追溯到系统或用户信任的根证书。每个证书的Subject与上一层Issuer必须匹配,签名合法,且在有效期内。
1.3 Android系统根证书存储
Android系统从版本4.0(API 14)起使用系统信任存储(System Trust Store),默认包含数百个知名根CA证书。存储位置:
- /system/etc/security/cacerts/ —— 系统预置根证书(.0格式,基于OpenSSL哈希命名)。
- /data/misc/user/0/cacerts-added/ —— 用户安装的自签或私有CA证书(仅影响当前用户,需用户确认)。
每个证书文件是一个PEM格式的公钥证书,文件名如 00673b5b.0。Android使用TrustManager API加载这些证书库。开发者可通过KeyStore或CertificateFactory读取。
注意:自Android 7.0(API 24)开始,默认不再信任用户安装的CA证书,仅信任系统CA。若需在调试代理(如Charles)中解密HTTPS流量,必须在
network_security_config.xml中显式声明user-ca信任域。
1.4 实战:查看证书链与验证根证书
使用OpenSSL命令行工具可抓取服务器证书链并手动验证。例如连接www.google.com:
openssl s_client -showcerts -connect www.google.com:443 -servername www.google.com
输出会包含完整的证书链(每段-----BEGIN CERTIFICATE-----到-----END CERTIFICATE-----)。提取根证书后,可验证其摘要是否与Android系统预置根证书匹配:
openssl x509 -in rootCA.pem -hash -noout
# 输出形如 "00673b5b"
# 检查 /system/etc/security/cacerts/ 中是否存在同名 .0 文件
1.5 常见证书验证错误
| 错误原因 | Android异常表现 | 解决方案 |
|---|---|---|
| 证书过期 | SSLHandshakeException + “Certificate expired” | 更新服务器证书 |
| 主机名不匹配 | SSLPeerUnverifiedException + “No name matching xxx” | 使用正确域名或配置HostnameVerifier |
| 自签证书不在信任库 | SSLHandshakeException + “Trust anchor for certification path not found” | 安装自签CA到系统/用户信任存储 |
| 中间证书缺失 | 同上 | 服务端配置完整的证书链 |
| Android 7+ 用户CA不生效 | 调试代理抓包失败 | 添加user-ca到network_security_config.xml |
1.6 安全建议与性能优化
- 证书固定(Pinning):在代码中固定叶子证书或中间CA公钥,防止证书擅自替换(第2章详述)。
- 根证书存储更新:通过Google Play Services定期推送,但Android 7+已集成自动更新模块。
- 减少握手延迟:使用TLS 1.3(0-RTT)、会话复用(Session Ticket)。
- 避免吊销检查延迟:OCSP stapling需服务器支持;离线缓存CRL可能造成陈旧。
1.7 内联SVG:TLS握手与证书链验证流程
下图展示了TLS 1.2握手过程中,客户端接收证书链并向上验证至根CA的完整路径。
1.8 本章小结
HTTPS证书链验证是Android安全通信的基石。理解TLS握手、证书链结构以及Android根证书存储机制,是后续实现证书固定(Pinning)与自定义网络库的基础。开发者应重点关注:
- 证书链缺失是线上常见错误,需强制服务端配置完整链。
- Android 7+ 用户CA隔离策略影响调试和私有CA部署。
- 证书固定不能替代底层验证,但能极大增强防中间人攻击能力。