2021年9月30日后Flutter在Android7上Let's Encrypt证书校验失败问题
故障原因
2021年9月30日,Let's Encrypt 服役多年的旧根证书 DST Root CA X3 正式到期。为了兼顾新旧设备的兼容性,Let's Encrypt 当时默认签发的是交叉签名证书链:用已过期的DST根证书交叉签名新的根证书 ISRG Root X1。
- 新版本安卓、苹果设备的证书验证逻辑会自动跳过链中已过期的根证书,匹配到系统预装的有效ISRG Root X1根证书,所以验证正常。
- Android 7.0及更早版本的系统存在两个核心问题导致验证失败:
- 系统预装的根信任列表中没有收录ISRG Root X1证书
- 证书验证逻辑只要检测到证书链中存在过期的根证书,就会直接终止验证,返回证书过期错误。
报错信息如下:
HandshakeException: Handshake error in client (OS Error: CERTIFICATE_VERIFY_FAILED: certificate has expired(handshake.cc:354))
解决方案
可根据使用场景选择对应适配方案:
- 服务端适配方案:在签发证书时指定优先使用纯ISRG Root X1证书链,剔除交叉签名的过期DST Root CA X3节点。以certbot工具为例,签发时添加参数
--preferred-chain "ISRG Root X1"即可生成符合要求的证书链。调整后旧安卓设备会直接匹配新根证书完成验证,该方案对上层业务无侵入,适用范围最广。 - 自有App适配方案:将ISRG Root X1公钥证书内置到App的信任锚点中,自定义HTTPS请求的SSL校验逻辑,优先使用内置的根证书完成站点证书校验。该方案不受系统根信任列表限制,兼容性最好。
- 普通用户临时解决方案:手动下载ISRG Root X1根证书,导入到安卓系统的用户信任证书存储中。该方案仅对系统自带浏览器、以及主动开启信任用户证书权限的App生效,Android 7及以上版本的第三方App默认不信任用户导入的证书,因此覆盖范围有限。
- 测试临时方案:开发调试阶段可以临时关闭HTTPS请求的证书校验逻辑,该方案存在严重的中间人攻击风险,严禁在线上生产环境使用。
内容的提问来源于stack exchange,提问作者Baker
相关产品推荐
相关产品推荐

