这是一篇针对 Android 开发者和后端运维团队的技术分析博客。我们将基于一个典型的、因 手动修改设备时间 导致的 SSL 握手失败 案例,深入剖析其背后的安全机制和证书链弱点。
一、现象描述与异常日志
当用户手动将 Android 设备时间修改到未来(例如 2025 年)时,应用内使用 Retrofit/OkHttp 发送的 HTTPS 请求会立即失败。奇怪的是,部分接口访问正常,部分接口访问异常。
异常请求的日志显示了一个 SSLHandshakeException,核心信息如下:
Code snippet
javax.net.ssl.SSLHandshakeException: Unacceptable certificate: CN=DigiCert Global Root CA...
Caused by: java.security.cert.CertificateException: Signature uses an insecure hash function: 1.2.840.113549.1.1.5
关键线索:
- 错误信息指向证书链中的
DigiCert Global Root CA。 - 根本原因明确指出:证书签名使用了 不安全的哈希函数(
1.2.840.113549.1.1.5,即 SHA-1 算法)。 - 核心测试: 当把设备时间调回 默认系统时间 后,所有接口恢复正常。
二、Bug 根本原因分析:双重安全检查失败
这个问题的本质是 设备时间偏差 和 服务器证书弱点 共同作用的结果,缺一不可。
1. 时间偏差:引爆器(The Trigger)
SSL/TLS 握手 的第一步是验证服务器证书的有效期。
-
证书都有一个明确的 “生效日期” 和 “截止日期” 。
-
当你将设备时间修改到 错误的未来 或 错误的过去 时,系统在检查证书有效期时发现:
- 尚未生效 (
Certificate Not Yet Valid) - 已过期 (
Certificate Expired)
- 尚未生效 (
-
结果: 证书有效期检查失败,握手必须终止。
2. SHA-1 签名:底层弱点(The Flaw)
根据日志,异常接口的服务器提供的证书链中,存在一个或多个证书使用了 SHA-1 签名算法。
-
官方政策: 自 Android 10 (API 29) 起,TLS 连接中签发给服务器的证书 使用 SHA-1 是 不被信任 的。浏览器和操作系统早已将 SHA-1 视为不安全并逐步淘汰(自 2017 年起)。
- 参考官方文档: Google 明确指出,在 Android 10 中, “Certificates signed with SHA-1 aren't trusted in TLS.” [1]
-
系统行为: 尽管证书链存在 SHA-1 弱点,但在 时间正确 的情况下,某些版本的 Android 可能会出于兼容性目的勉强接受。
-
双重失败机制: 当时间检查失败(引爆器)后,Android 的安全模块(
TrustManagerImpl内的ChainStrengthAnalyzer)会切换到最严格的校验模式,此时它不仅发现时间错误,还发现了证书链中的 SHA-1 弱点(炸弹),并最终以“不安全的哈希函数”作为最明确的错误原因向上抛出异常。
3. 为什么有的接口正常?
接口访问差异的唯一解释是:证书链不同。
- 正常接口: 它们连接的服务器使用了 符合现代安全标准(SHA-256 或更高) 签名的证书链,不包含任何 SHA-1 弱点。因此,即使时间错误,系统的安全检查也能在遇到时间错误时进行优雅降级或兼容处理,不触发致命的 SHA-1 拒绝。
- 异常接口: 它们连接的服务器使用了包含 SHA-1 弱点 的证书链,一旦时间偏差触发了严格检查,立即暴露并拒绝连接。
三、Charles 代理绕过机制分析
用户发现通过 Charles 代理 访问时,即使时间错误,异常接口也能恢复正常。这进一步证实了问题根源在证书链。
Charles 等抓包工具充当 中间人 (MITM) 代理,其工作流程如下:
- 替换证书链: Charles 在设备和服务器之间截断 HTTPS 流量。
- 伪造证书: 它会动态生成一张伪造证书给客户端 App。这张证书由安全的 SHA-256 算法签名,并且由已安装在设备中的 Charles Root CA 签发。
- 绕过检查: App 收到这张 “干净” 的 Charles 证书,发现它在有效期内(由 Charles 动态设置)且使用了安全的 SHA-256 签名。因此,它成功通过了 Android 的严格安全检查,完成了握手。
结论: Charles 的伪造证书“干净”无弱点,成功绕过了服务器原始证书链的 SHA-1 缺陷。
四、解决方案与官方建议
要彻底解决此问题,唯一的办法是 消除服务器证书链中的 SHA-1 弱点。
| 责任方 | 解决方案 | 官方建议/参考 |
|---|---|---|
| 服务器/运维 | 升级证书链,使用 SHA-256 算法 | 必须 确保所有服务器的 SSL/TLS 证书(包括服务器证书和中间 CA 证书)都使用 SHA-256 或更强的哈希算法签名。SHA-1 已被行业淘汰多年。 |
| Android 客户端 | 使用自动时间同步 | 始终建议用户开启 “自动确定日期和时间” ,确保设备时间准确,这是避免因有效期检查失败引发一系列安全错误的最佳实践。 |
| Android 客户端 | 使用 Network Security Configuration (NSC) | 可以使用 NSC 配置文件来定义应用信任的 CA 集合、执行证书固定,或者配置 TLS 协议行为,从而更严格地控制安全连接。[2] |
【DigiCert 证书提示】 您的日志中提到的 DigiCert Global Root CA 属于 G1 根证书体系,DigiCert 早就建议并推动用户迁移到 G2/G5 根证书,以确保使用最新的安全标准。请确保服务器使用的证书是基于这些新标准的。[3]