跳过正文
  1. 博客/

Go X.509 标准库中 SKID 的故事

·273 字·2 分钟·
Go X509
目录

SKID 是什么
#

1999 的 RFC 2459 Internet X.509 Public Key Infrastructure Certificate and CRL Profile 中定义了 2 个 X509 v3 证书的扩展概念:

  • Authority Key Identifier,简称 AKI / AKID
  • Subject Key Identifier,简称 SKI / SKID

简单来说,它们是一种标识,对应的意义分别是:

  • AKID,用来识别 被特定私钥签发的证书 – 应使用签发者(issuer)的公钥进行生成
  • SKID,用来识别 包含特定公钥的证书 – 应使用证书中的公钥进行生成

所要确保的就是,证书 Y 中的 AKID,必然是签发证书 Y 的 CA 证书 X 的 SKID。

证书路径/链条构造图

以上图 1 的证书路径/链条构造为例,Root CA 证书 1 的 SKID 就是 CA 证书 2 的 AKID,而 CA 证书 2 的 SKID 又是 CA 证书 3 中的 AKID。

RFC 明确要求所有被 CA 签发的下级证书都必须存在 AKID,只有 “自签发” 的证书才可以作为特例而省略填充这个字段。而 SKID 则是要求必须出现在 CA 证书中。

这两个扩展设计出来的意义主要是为了更方便的进行证书路径/链条的构建,通俗点说的话,就是给定一个被签发的证书 X,可以通过其 AKID 快速的在 CA Pool 中找到签发它的 “Parent"。

这只是用来进行对象的快速查找,具体找到的关联对象是不是真的 “Parent” 还需要进行校验签名的判断。

想要用 SKID 查询证书的话可以去这里:crt.sh

高级搜索
搜索结果

具体 SKID 应该如何生成,标准中定义了两种 “常用方法”:

两种建议的生成方式

OpenSSL 在这个对文档进行补充的 提交 中专门说明了生成 SKID 的实现方法是按照 RFC 5280 的 4.2.1.2. (1),其实也就是 RFC 2459 的 4.2.1.2. (1). 因为虽然 RFC 2459 逐步被新发布的 3280、5280 所淘汰,但是新 RFC 中关于 AKID、SKID 的这部分没什么变更。

虽然没有去看具体的 C 代码,但我想既然是专门在文档补充这个细节,大概率表示 OpenSSL 应该一直都是使用这个方法进行 SKID 的生成。

其实标准中在 “常用方法” 的定义下紧接着说到 Other methods of generating unique numbers are also acceptable.,但我不禁怀疑是否真的有使用自定义方法进行实现的软件。直到我偶然了解到 Go 中 SKID 的故事。

Go 中的故事
#

在 Golang 1.15 之前,标准库 x509 中的 CreateCertificate 方法不会对 CA 类型的证书自动的添加上 SKID,这个行为明显是违反了 RFC 的要求。这个情况是在 2018 年被发现的:crypto/X509: add SubjectKeyId automatically when IsCA is true。按这个 issue 中的讨论看,Go 团队认为将其在 1.14 中修复是赶不上了,于是直到 2020 年 4 月才有了修复的提交。

然而,这个提交的代码实现是有点。。。 特别的。 作者选择直接将 SPKI 整个结构体 marshal 出的 bytes 进行 hash,并不是上面章节中提到的 RFC 定义的两种常用方法的任意一种。

虽然按 RFC 的意思,你搞自定义的也不是不可以。但要真按这个思路生成出来的证书,在与其他使用 RFC 的「常用方法」进行 SKID 生成的实现(比如 OpenSSL)进行交互时就很难说会不会产生意料之外的兼容性问题。

好在这个情况仅仅过了 2 个月就被发现:crypto/X509: SKID generation is over full spki,rather than just the subjectPublicKey,也赶在 1.15 正式发布前完成了修复。

diff details

可以看见最终是选择了第一种方法,对公钥的 bytes 做 SHA-1 的 HASH。

吐槽
#

比较难以想象,这是一个 2020 年 6 月 9 号才修复的问题,并且正式的 1.15 版本是 2020 年 8 月 11 号才发布的。

RFC 2459 是 1999 年的东西了。就算以 2008 年的 RFC 5280 做标准,在随后漫长的互联网产业扩张并「造福世界」的过程中,各大证书厂商/开源工具等对于 SKID 这个东西的操作可以说应该是已经相当成熟。

虽然 Golang 是一个比较年轻的语言,这也只是实现 RFC 标准的过程中出现的小瑕疵,不是什么很致命/关键的错误,但联想到之前的 OpenSSL Heartbleed 事件,是真的越是底层的基础设施就越是这样得不到该有的关注和投入,还是我太想当然了呢?