为什么 Chrome 会说你的 SHA-2 证书链是“肯定不安全的”

为什么 Chrome 会说你的 SHA-2 证书链是“肯定不安全的”

假如你已经完全配好了你的 SSL:使用了强加密算法、禁用了废弃的协议,而且你提供了 100% SHA-2 的证书链。SSL Labs 给了你一个 A+ 评分,shaaaaaaaaaaaaa.com 也没发现你使用了 SHA-1。但是,有些情况下,当你访问你的网站时,Chrome 仍旧会在 URL 栏处显示一个红叉,并且说你的网站提供了 SHA-1 证书,是“ 肯定不安全的 ( affirmatively insecure ) ” 的:

URL bar showing red cross through ‘https’

这可能吗?不幸的是,有可能。你的服务器所发送的证书也许并不是你的浏览器所使用的。在迁移到 SHA-2 的过程中不应该是这样的,但是由于某些 CA 糟糕的做法和用户使用了老旧的软件,有时候会出现这样的问题。具体听我一一道来。

背景知识:证书链如何工作

一个 SSL 证书必须被 证书授权中心 ( certificate authority,CA ) 签名才是可信的。在最简单的情况下,网站的证书( 终端证书 ( end-entity certificate ) )是由浏览器所信任的 CA 的( 根证书 ( root certificate ) )直接签名的。

简单 PKI: 终端证书由根证书直接签名

浏览器校验直接由根证书签名的证书很简单:浏览器在它的根证书库里面查找终端证书的签发者,如果找到,就使用该根证书的公钥来校验终端证书的签名。

然而,终端证书很少会由根证书进行签名。相反的,终端证书是由一个“ 中间证书 ( intermediate certificate ) ”(有时候也称之为 下级 CA ( subordinate CA ) )进行签名的,而中间证书则由根证书进行签名。

典型的 PKI 是使用一个中间证书

校验一个由中间证书签名的证书是困难的。浏览器不能简单地在它的根证书库中找到签发者,因为该证书并不是由根证书签名的。相反,浏览器需要找到签名该证书的中间证书,需要的话还得迭代这个过程(中间证书也许是由其它的中间证书签名的),直到沿着这个证书链找到根证书。而事实上更复杂,因为也许从终端证书到根证书有几种可能的链路。

让浏览器找到中间证书链的最简单的办法是,让服务器告诉它。这就是为什么当你部署一个 SSL 证书时,不仅仅需要你自己的证书,还需要中间证书。不过,浏览器通常会缓存中间证书,也许会使用一个缓存的证书而不是使用由你的服务器提供的证书。这就是为什么在你全部使用了 SHA-2 之后, Chrome 也许还会显示一个红叉的原因:它并没有使用你的证书链上提供的证书,而是使用了其所缓存的 SHA-1 的证书链。

理论上,这是可以避免的,如果 CA 可以正确操作、用户也运行最新的软件的话。不幸的是,现实并不总是这样,个别情况下已知导致这个问题的原因有两个。

(注意:澄清一下,生成证书链的并不是 Chrome ,而是 Chrome 交由操作系统的加密库构建证书链。加密库在 Windows 上是 CryptoAPI,Linux 上是 NSS。)

问题一:将 SHA-1 中间证书用 SHA-2 重用

当 CA 迁移到 SHA-2 时,它可以通过对已有的公钥用 SHA-2 重新签名,从而重用已有的中间证书,或者也可以生成一个带有新的公钥和 主题名 ( subject name ) 的新中间证书。

两种新的 SHA-2 签名方式

第一种方式是错误的。虽然 CA 可以用已有的中间证书使用 SHA-2 重新签名,但是浏览器也许会缓存着带有旧的 SHA-1 签名的中间证书。如上面解释的,浏览器可以忽略由服务器提供的链证书,即便服务器发送了带有新的 SHA-2 签名的中间证书,客户端依然会使用其所缓存的 SHA-1 中间证书来构建证书链。CryptoAPI 就是这样的。

服务器发送了 SHA-2 证书,浏览器使用了缓存的 SHA-1 证书

而第二个办法就避免了缓存证书链的问题。因为 CA 生成了一个新的中间证书,有新的名字和公钥,浏览器不可能有用 SHA-1 签名的旧版本。这就是为什么 Ballot 118 在 CA/Browser Forum 这样说:

SHA-2 下级证书绝不应该链到 SHA-1 的 CA 下级证书上。

不幸的是,有些 CA 根本没在意这个忠告,比如某个 CA ,StartCom,仍然使用 SHA-1 中间证书签发 SHA-2 终端证书。虽然他们提供了一个 SHA-2 签名的中间证书,但是如果浏览器已经有一个缓存的 SHA-1 版本时就会有问题。

幸运的是,SSLMate 的两个 CA 都正确地从新的 SHA-2 中间证书签发证书,所以,SSLMate 的客户不需要担心这个。

问题二:过期的 NSS 和交叉签名的根证书

有时候根证书自身也由其它的根证书签名,通常这称之为 交叉签名 ( cross-signing ) 。这提供了一个抵达可信根证书的另外一条路径,交叉签名可以让一个不在你的浏览器的可信证书库中的新根证书也可以工作。当浏览器开始信任一个根证书时,交叉签名的证书就不再需要了。然而,和中间证书一样,交叉签名的证书也是可以缓存的,而在 NSS 中一个 bug 会导致 Linux 上的 Chrome 使用缓存的交叉签名的根证书,即使有更短和更新的证书链存在。如果这个缓存的交叉签名证书正好使用 SHA-1,Chrome 就会使用这个有问题的证书链并显示一个红叉,而不管你的服务器是否发送了全都使用 SHA-2 的证书链。

NSS 使用缓存的交叉签名证书构建证书链

这个 bug 在 NSS 3.17.4 中修复,发布于1/28。不幸的是,Debian 更新 NSS 软件包非常缓慢。这个 bug 在2014/12/30 就提交到了 Debian 的 bug tracker 上了,但是直到 5/13 都没在 Debian Unstable 中修复。同时,Debian Stable (Jessie)继续用着 NSS 3.17.2 。Debian 安全团队排除了会通过安全更新修复它,而且看起来包维护者也不像是会快速响应将这个修复加入到即将发布(本文写作时)的 Debian Stable 中。Ubuntu 则不同,将此作为安全问题,在 2/19 给其所有发行版发布了更新包。

不幸的是,CA 不能为使用过期 NSS 的用户做些什么。直到 Debian 为其稳定发行版发布更新的 NSS 包之前,Debian 上的 Chrome 用户会一直看到许多这样的红叉:

URL bar showing red cross through ‘https’

或者,如果证书在 2016 年失效的话是这样:

URL bar for https://bugs.debian.org, with orange alert symbol

**更新:**由 SSLMate 的 Andrew Ayer 提供的 NSS 更新包,放到了 2015/9/5 发布的 Debian Jessie 8.2 中。