根据 RFC 5280 验证 SSL 证书链:我理解正确吗?

信息安全 tls 证书 验证 x.509 客户
2021-09-06 11:36:48

我们正在用 SHA1 哈希替换证书,因为谷歌采取了让它们在 Chrome 中显得不那么安全的举措。替换证书使用与我们当前使用的不同的中间 CA,但使用相同的根 CA。在测试期间,我们注意到我们的 SSL 客户端无法使用我们不太明白的新证书验证链。

以下是具体情况:

目前,服务器发送的链如下所示:

  • 根 CA,由同一机构的服务器 CA X 签名(附带服务器证书)
  • 中间 CA 1(附带服务器证书)
  • 服务器证书

在我们信任的 SSL 客户端中:

  • 根 CA,自签名,但相同的 CN 和相同的密钥(相同的滑动)!(从权威机构下载;与 Mozilla 包含在其产品中的相同)
  • 中间 CA 1(与服务器发送的相同)

这适用于当前的证书链,但只是因为我们信任中间 CA 1。如果我们删除它,它会失败,因为服务器 CA X 不受信任。新证书显然不起作用,因为它们是由不同的中间 CA 签名的。

现在,通过阅读 RFC 5280 第 6.1 节,我的印象是我们的 SSL 客户端不遵守标准,因为根据我对证书路径验证的理解(简化):

  1. 从信任锚开始;验证它是否仍然有效(时间)等。
  2. 对于以下证书:检查前一个证书的 CN 和密钥是否与当前证书的颁发者 CN 和密钥匹配(并检查 pathlen 约束等...)
  3. 最后一个证书不能是 CA 证书

关键是:我们的 SSL 客户端无法验证链,因为它被配置为用作信任锚的根证书与服务器提供的根证书具有不同的颁发者。但是,两者的 CN 和 key 是相同的。根据我对 RFC 5280 中所读内容的理解,SSL 客户端不应该关心其信任锚的发布者。链内的证书中也没有定义可以强制执行此类操作的策略。但是,我不知道我们的 SSL 客户端是否会强制执行会导致这种情况的其他策略(不是我的系统)。

所以我的问题是:我们的 SSL 客户端在这种情况下是否正确运行?当然:为什么?

提前致谢

编辑:我知道服务器应该只发送中间 CA 证书而不是根证书,但是,我们使用权威提供的作为证书旁边的 cabundle,其中包括根证书。此外,据我所知,在这种情况下,客户端应该忽略服务器发送的根证书。

同样奇怪的是,客户端的行为就好像它验证了从服务器证书到它可以信任的东西然后停止的链,而规范说它应该验证从信任锚到服务器证书的链。

Edit2:根 CA 证书的不同“版本”都具有相同的 SKID - 它们仅在发行者和序列号方面有所不同(当然)。

另外,刚刚想到,从第一次编辑的信息来看,SSL 客户端似乎需要信任的根证书和链在其发行者中相同,并且可能需要序列号来接受它。

2个回答

X.509,还有SSL/TLSTLS 期望服务器发送一个 X.509 证书链,客户端将从中提取服务器的公钥。然后事情就不同了:

  • 在纯 X.509 中,服务器应该将其证书发送给客户端进行验证;可能,服务器可能会添加一堆无序的额外证书,这些证书可能对客户端有用,基本上是一些可能相关的中间 CA。客户端仍然可以自由地以它认为合适的任何方式验证证书,特别是通过任意链构建。例如,在CMS(以前称为“PKCS#7”)中演示了此方法,其中签名文档 ( SignedData) 引用签名者的证书,并且可选地包括额外证书的无序集 (ASN.1 " SET")。

  • 在 SSL/TLS 中,事情并非如此。相反,服务器应该发送要使用的确切链;明确允许服务器省略根 CA,仅此而已。如果链与此模式不匹配,任何客户端都有权(至少在历史上,如果不是法律上的话)拒绝服务器的证书。当然,任何客户端都可以努力尝试以 X.509 方式验证服务器的证书,并使用自己的路径构建;但是有些 SSL 客户端并没有做出这样的努力。

RFC 5246 的相关摘录在附录 F.1.1 中

如果服务器经过身份验证,则其证书消息必须提供有效的证书链,以通向可接受的证书颁发机构。

这是赋予 SSL 客户端“权利”以毫不客气地拒绝无法“按原样”验证的服务器链的段落。

在您的情况下,服务器发送的链是:

    根-X -> CA1 -> 服务器

其中“Root-X”是“由服务器 CA X 签名的根”。惰性客户端尝试验证该链两次:

  1. 第一次尝试是基于链包含根 CA 的假设。客户端在自己的根 CA 列表中没有找到“Root-X”,因此失败。

  2. 第二次尝试是基于链不包括根 CA 的假设客户端扫描其根 CA 以查找Root-X的颁发者(这是关键点)。它没有找到任何东西,因此失败。

为了验证服务器发送的链,客户端必须稍微不那么懒惰,并尝试在其自己的可信 CA 集中找到 CA1 的颁发者。这将需要第三个假设,即服务器发送的链确实包含根 CA,但不包含“正确的”。

我想您失败的 SSL 客户端是上面阐明的“惰性客户端”。为确保,请尝试以 Internet Explorer 作为客户端连接;尽管 IE 有很多缺点,但在证书验证方面并不懒惰。IE 将尝试从服务器发送的证书、它自己存储的证书以及它可以下载的证书重建一个链。值得注意的是,任何证书都可能包含指向其颁发者证书的 URL(这是颁发机构信息访问扩展的一部分);IE(实际上是一般的 Windows)会兴高采烈地遵循这样的 URL 并收集中间 CA(仅限 HTTP,而不是 HTTPS,以避免令人讨厌的循环)。

如果您因此可以确认问题确实是一个惰性客户端,那么可能的解决方法包括以下内容:

  • 更改服务器发送的链,使其包含自签名根,或仅以 CA1 结束。
  • 在客户端中安装“服务器 CA X”证书作为受信任的 CA。
  • 修改/配置客户端以免偷懒。

当然,您的链很可能在其他方面不正确,如果没有实际看到证书就很难评估。路径验证算法很复杂,因此可能有另一个扩展不让客户端满意(例如,一个证书可能正在使用Name ConstraintsSSL 客户端实现只是阻塞的扩展)。可能客户端不是懒惰,而是坚持要确定路径中所有证书的吊销状态,并且某些 CRL 或 OCSP 响应者已过期或无法访问。证书验证可能会失败的方式有数百种。

提供的链不应包含根,如果包含,客户端必须忽略它。X.509 信任的一个关键特性是它需要预先知道的根(或信任锚)。这不是你的问题的原因。顺序是颠倒的,服务器证书应该是第一个,然后是它的签名者(如果需要,还有它的签名者等),这可能会在某些平台上引起问题,但请继续阅读。

当代客户不再依赖于通过 DN(即颁发者的证书名称)追上链。从证书的AKID(授权密钥标识符)开始,父级是具有匹配SKID的证书。SKID(主题密钥标识符)旨在成为(统计上的)唯一标识符,通常它是从公钥的哈希派生的,但不是必须的。出于验证的目的,它只是一个不透明的标识符(即验证不会重新计算它,它只比较值)。尽管人类可读的名称没有改变,但标识符似乎很可能已经改变。

链的检查必须从服务器证书开始,并在“顶部”向根部工作,这通常是“向上”。这与链的呈现顺序相同。每个证书“知道”它的签名者(CN 和 SKID),对层次结构一无所知。

这稍微扩展了验证过程:证书链检查,并给出了一个使用 OpenSSL 重现典型验证过程的示例:https ://serverfault.com/questions/541262/checking-the-issued-and-expiry-dates -for-the-certificates-involved-a-证书

另一个可能的问题是什么构成了“顶部”或“信任锚”。一些 CA 具有自签名根证书 (AKID==SKID),一些使用交叉签名根(签名证书属于另一个 CA)。停止跟踪链的确切位置是客户端密钥库的属性,通常借助特定分类中的 CA 证书。