记一次 Let's Encrypt https 证书在旧设备上显示错误的情况

前言

前段时间测试人员在测试一个新的站点的时候, 有发现在旧的 ios 上,会出现页面加载失败的情况, 会报 ssl 错误:

1
The certificate for this server is invalid.

但是在其他的比较新的 ios 或者 android 上, https 的页面又显示正常。 而且证书看起来也没有过期。

后面查了一下,因为这个站点,我们那时候用的是 Let’s Encrypt 签发的免费证书, 而这个证书在一些比较旧的系统上,比如 iOS < 10 , macOS < 10.12.1 会有证书的不信任问题。

接下来我们简单的盘一下为啥会这样子

HTTPS 和 CA 机构 (证书颁发机构)

在说到 Let’s Encrypt 这个 CA 机构之前,我们得先了解一下什么是 CA 机构。 而在讲 CA 之前,要先了解一下 HTTPS 的加密方式, 这一部分资料网上很多, 我这边长话短说

HTTPS 加密方式

对称加密

所谓对称加密,就是收发双方共用同一套密钥。

对称加密就是谍战片中的电报密码本。第一个汉字对应一个密码,收发双方共用一套密码本,所以才能加密和解密。如果能拿到或者破解敌人的密码本,就能监听敌人的电报!所以要保护好密码本!这种需要密码本的加密仅仅适用于少数人之间的通信。你不可能把密码本发给每一个人。但是我们的网站可能有各种各样的人来访问,怎么才能实现加密通信呢?这就需要用到非对称加密。

非对称加密

所谓非对称加密,就是利用数学方法生成一对密钥,一个对外公开,所有人都能看,我们称之为公钥;另一个自己妥善保存,不可轻易示人,我们称之为私钥。

如果一方要通信,可以使用对方的公钥对数据进行加密,对方再用自己在私钥解密。这里跟对称加密最大的不同就是要用到公、私两个密钥。因为私钥不需要发给别人,所以非常安全。

结合起来

那我们可以直接使用非对称加密技术来加密HTTP通信吗?并不能!因为非对称加密虽然安全,但计算量很大,加密和解密过程都比较慢。

对称加密倒是快,但不安全。于是人们就把这两种加密方法结合起来,形成了现在通行的 SSL 或者 TLS 加密体系。

其核心是在通信之前随机生成一份密码本,然后用非对称加密之后发给对方。这样双方就有同一份密码,然后可以用对称加密进行通信。每次都可以生成新的密码,用完就扔,保证安全性。

所以你会发现在整个 SSL/TLS 的加密体系上, 其实根本没有 SSL 证书介入, 证书在这个过程中,没有起作用!!

SSL 证书 和 CA

那么证书的作用是啥,就是 公钥验证

前面说非对称加密需要把公钥公开给大家。比如我的个人网站 kebingzao.com 把自己的公钥公开,所有想访问我博客的朋友需要根据我公开的公钥来给我发加密数据(协商密码)。可是网上有另一个不怀好意的人,它也公开了一枚公钥,也声称这是 kebingzao.com 的公钥。那想访问我博客的用户怎么确定到底哪个公钥才是 kebingzao.com 的真正公钥呢?这就需要 SSL 证书。

而这个 SSL 证书 就是公证机构开的证明,证明某个公钥是某网站的公钥, 其他的声称也是该网站的公钥的,他没有这个证明,他就是假的, 系统就不会信任。

这里的公证处就是所谓的 CA 机构。我可以按一定的格式,把自己的公钥(不是私钥,私钥一辈子都不能给别人!)、网站域名,甚至是组织信息填写到一个文件中发给 CA,CA 会用自己的私钥对这个文件进行签名,而这个签名可以根据CA的公钥来验证。所以说,CA也有一对私钥和公钥。

那问题又来了,会不会有人冒充 CA 来发布伪造的公钥呢?确实会。这个问题是由操作系统厂商来解决的。不论是 windows、linux、android 还是 macos、ios,都会内置一份 CA 公钥列表(也叫CA根证书),只有系统内置的CA签发的证书才是有效证书!

比如 windows 10 可以查看“受信任的根证书颁发机构”列表:

这些证书内置在操作系统中,通常作为更新操作系统的正常过程的一部分进行更新。

Let’s Encrypt

Let’s Encrypt 也是一家 CA 机构,因为它可以免费签发证书, 他为啥可以做到免费呢? 主要是以下两点:

  1. 免费证书都是 DV 证书。这种证书仅需要验证域名所有权。只要你控制某个域名,就可以给你签发证书。整个过验证过程完全是自动化的,不需要人工介入,所以也没什么成本
  2. Let’s Encrypt 是 ISRG 运营的非营利性标准化互联网组织,受到主流厂商支持

通过这两点,他可以做到免费签发证书,不过免费证书也有一些问题:

  1. 有效期只有 3 个月 (正常花钱购买的,都是有效期一年)
  2. 部署相对复杂,需要部署自动化域名验证系统(ACME协议),不过这一块很多可用的脚本可以用
  3. 旧的系统不支持,表现出来就是这个 CA 机构没有在操作系统的“受信任的根证书颁发机构”列表,所以他签发的证书就会不被信任,这个也是本次发生的问题所在。

我的个人站点就是用的 Let’s Encrypt 签发的证书, 而且因为是寄托在 github 上面, 所以连 3 个月一次的证书替换也都不用我处理, github 会帮我处理

交叉签名(Cross-Signing)

Let’s Encrypt 早在 2015 年就有自己的根证书 ISRG Root X1, 有效期到 2035 年 6 月 4 日

再加上这几年 Let’s Encrypt 发展的很快(免费是真的香), 现在主流的系统基本上都在 “受信任的根证书颁发机构”列表都有加上 Let’s Encrypt 的根证书, 有些之前没有的,也会通过系统更新的方式将其加入进去。

而一些比较旧的系统也会通过交叉签名的方式来让其可以识别自家的证书。 事实上,每一个新的 CA 机构都要通过这一步骤来让这些旧系统或者长期不升级的系统来支持自家的证书,Let’s Encrypt 也不例外

交叉签名本质上是通过使用放置在另一个 CA 中的信任使您成为 CA

为了尽快开展业务,Let’s Encrypt 让 IdenTrust(老牌 CA 机构) 使用自己的根证书 DST Root CA X3 签发一张 Let's Encrypt R3 中间证书。因为主流系统都信任 DST Root CA X3,所以自然也会信任 Let’s Encrypt R3,进而信任 Let‘s Encrypt 签发的其他证书。

与此同时,Let’s Encrypt 一直努力让主流系统信任它自己的根证书 ISRG Root X1。到了 2021 年,主流系统基本都把 ISRG Root X1 加到自己的信任列表了。

Let’s Encrypt 开始脱离 IdenTrust 自立门户。而就在2021年的9月30号,DST Root CA X3 根证书过期了。于是,Let’s Encrypt 原先的中间根证书 Let's Encrypt R3 及其签发的所有证书都过期了。

不过这都是计划内的事。Let’s Encrypt 已经使用自己的 ISRG Root X1 根证书为自己的 Let's Encrypt R3 签名认证,继续签发证书。

但是对于那些原来只能信任 IdenTrust 证书从而一起信任 Let’s Encrypt 证书的旧系统来说(iOS < 10 , macOS < 10.12.1), 那就彻底的不能用了。

甚至就包括 2.3.6 <= Android < 7.1.1 这部分系统。为了解决这个问题,Let’s Encrypt 的工程师想到了一个既不违反标准,又能解决问题的办法:

DST Root CA X3 还没过期的时候,使用 DST Root CA X3ISRG Root X1 做交叉签名,而且签名的有效期定在了 2024 年。这里交叉签名的有效期设在了 DST Root CA X3 的过期时间之后。安卓系统在检查证书信任链的时候不会校验根证书是否过期,这就实现了在 DST Root CA X3 过期之后,老系统继续信任 ISRG Root X1 的效果。有人说这是老版本 Android 系统的 bug,其实不然。这种做法并不违反相关的互联网标准。

上图中最左边的链是 Let’s Encrypt 早期使用 DST Root CA 签发的中间证书,最右边的链是直接使用自己的 ISRG Root X1 签发的中间证书,而中间则是现在到 2024 年的过渡方案,使用 DST Root CA X3 交叉签名 ISRG Root X1,然后再签发 Let's Encrypt R3 中间证书。无论如何,解决了 2.3.6 <= Android < 7.1.1 这部分系统(从百度统计的数据来看大约占 10%)的兼容性问题。但代价则是在证书链里多了一个 DST Root CA X3 证书,这会减慢 HTTPS 连接握手速度。两害相权取其轻。再过三年,估计绝大多数系统都会支持 ISRG Root X1,到时个就可以去掉证书链中的 DST Root CA X3

通过这种方式,就可以为这些 android 设备成功再续费 3 年了, 这操作是真骚啊。 但是其他的老系统就没有这种待遇了,在 2021-09-30 DST Root CA X3 过期之后,Let’s Encrypt 的证书就不再信任了, 也就是文章开头出现的那个问题。

Let’s Encrypt 证书的兼容性

以下的这些老系统就会影响到:

  • OpenSSL <= 1.0.2
  • Windows < XP SP3
  • macOS < 10.12.1
  • iOS < 10 (iPhone 5 及以后机型可以升级到 iOS 10)
  • Android < 7.1.1 (但如果提供 ISRG Root X1 交叉签名,则 >= 2.3.6 将起作用)
  • Mozilla Firefox < 50
  • Ubuntu < 16.04
  • Debian < 8
  • Java 8 < 8u141
  • Java 7 < 7u151
  • NSS < 3.26
  • Amazon FireOS (Silk Browser)

解决方案

最简单的当然是不用 Let’s Encrypt 证书,换成其他老牌 CA 签发的证书,比如 GoDaddy (我们就是用这种方式,氪金大法)。

如果是解决单机问题的,也可以删除 IdenTrust DST Root CA X3 根证书并手动安装 ISRG Root X1 根证书(不是交叉签名的)。


参考资料