3、X.509证书标准:证书结构、版本号、序列号、签名算法、颁发者、有效期、主体、公钥信息、扩展项

X.509 是 PKI 体系的基石。说白了,它就是定义了一张「数字身份证」长什么样、每个字段写什么的标准。我最早接触 X.509 是在做 HTTPS 证书解析的时候,当时被 ASN.1 编码搞得一头雾水。后来才明白,只要把证书的字段结构搞清楚,剩下的就是编码问题了。

这一节,我们就把 X.509 证书的每个字段拆开来看。你想想看,一张证书从签发到验证,每一步都依赖这些字段的正确性。任何一个字段出错,证书链就可能断裂。

3.1 证书结构总览

一张标准的 X.509 v3 证书,包含三个主要部分:

  • 证书体(TBSCertificate)—— 真正要签名的数据
  • 签名算法标识—— 告诉你怎么验证签名
  • 签名值—— 防篡改的保证

我个人习惯把证书体比作「身份证上的信息」,签名值比作「公安局的钢印」。钢印盖上去,信息就不能改了。

X.509 证书三层结构 X.509 v3 证书结构 TBSCertificate(证书体) 版本号 (Version) 序列号 (Serial Number) 签名算法 (Signature) 颁发者 (Issuer) 有效期 (Validity) 主体 (Subject) 公钥信息 (Subject Public Key Info) 扩展项 (Extensions) — v3 新增 签名算法标识 (Signature Algorithm) 签名值 (Signature Value) 签名算法在证书体和签名算法标识中重复出现,用于防止算法替换攻击

3.2 版本号

版本号告诉解析器:这张证书用的是哪个版本的 X.509 标准。目前有三个版本:

版本 编码值 特点
v1 0 最基础,没有扩展项
v2 1 引入了主体和颁发者唯一标识,但很少用
v3 2 支持扩展项,现代证书的标准

现在你几乎看不到 v1 和 v2 的证书了。我记得有一次在遗留系统里发现一张 v1 的根证书,CA 说用了十几年没换过。嗯,这种证书该换了,没有扩展项意味着很多安全特性都用不了。

小提示: 解析证书时,版本号字段的编码值是 0、1、2,但显示时通常写成 v1、v2、v3。别搞混了。

3.3 序列号

序列号是 CA 给每张证书分配的唯一编号。它在同一个 CA 下不能重复。我见过一些 CA 用时间戳加随机数生成序列号,也有直接用递增整数的。

序列号有几个关键作用:

  • 唯一标识 —— 配合颁发者 DN,可以全局唯一确定一张证书
  • 吊销查询 —— CRL 和 OCSP 都靠序列号定位证书
  • 日志审计 —— 出了问题,查序列号就能追溯到具体证书
注意: RFC 5280 要求序列号必须是正整数,且至少 20 位(即 ≥ 2^159)。我曾经见过一个 CA 用 4 字节的序列号,结果被安全审计打了回来。序列号太短,容易被暴力枚举。

3.4 签名算法

这个字段出现在两个地方:证书体里和证书体外面。为什么重复?

说白了,就是为了安全。证书体里的签名算法告诉验证方「我是用什么算法签的名」,外面的签名算法标识则用于快速定位算法。如果两者不一致,说明证书可能被篡改过。

常见的签名算法组合:

算法 OID 哈希算法 签名算法 状态
1.2.840.113549.1.1.5 SHA-1 RSA 已弃用
1.2.840.113549.1.1.11 SHA-256 RSA 推荐
1.2.840.10045.4.3.2 SHA-256 ECDSA 推荐
1.2.840.10045.4.3.3 SHA-384 ECDSA 推荐

我个人习惯优先选 ECDSA 算法。同样的安全强度,密钥更短,签名也小。RSA 4096 的签名有 512 字节,而 ECDSA P-384 的签名只有 96 字节。在证书链较长时,这个差距很明显。

3.5 颁发者

颁发者字段记录的是签发这张证书的 CA 的身份信息。它用 DN(Distinguished Name)表示,包含一系列属性:

  • C —— 国家(Country),如 CN
  • ST —— 州/省(State/Province)
  • L —— 城市(Locality)
  • O —— 组织(Organization)
  • OU —— 组织单元(Organizational Unit)
  • CN —— 通用名称(Common Name)

举个例子,一张由 Let's Encrypt 签发的证书,颁发者 DN 可能是:

C=US, O=Let's Encrypt, CN=R3

你想想看,验证证书链时,子证书的颁发者 DN 必须和父证书的主体 DN 完全一致。我曾经排查过一个证书链验证失败的问题,折腾了半天,发现是颁发者 DN 里多了一个空格。嗯,字符串比较就是这么严格。

3.6 有效期

有效期包含两个时间戳:

  • notBefore —— 证书生效时间
  • notAfter —— 证书失效时间

格式是 UTC 时间或 GeneralizedTime。我建议你在解析时统一转成 UTC,避免时区问题。

关于有效期,有几个坑要注意:

避坑指南:
  • 根证书的有效期通常很长(10-20 年),但私钥的轮换周期应该更短
  • 中间证书的有效期一般在 5-10 年
  • 终端证书现在普遍是 90 天(Apple/Google 推动的短寿命趋势)
  • 验证时不仅要检查当前时间是否在有效期内,还要检查证书链上所有证书都有效

我曾经遇到过一个案例:根证书还有 10 年才过期,但中间证书已经过期了。结果所有由这个中间证书签发的终端证书都验证失败。这就是典型的「链上有一环断了」。

3.7 主体

主体字段记录的是证书持有者的身份信息。格式和颁发者一样,也是 DN。对于终端证书,主体通常包含网站域名或用户身份信息。

举个例子,一张百度网站的证书,主体 DN 可能是:

C=CN, ST=Beijing, L=Beijing, O=Beijing Baidu Netcom Science Technology Co., Ltd, CN=baidu.com

这里有个关键点:主体 CN 和 SAN(Subject Alternative Name)的关系。早期浏览器只检查 CN,但现在 CN 已经被弃用了,浏览器只看 SAN 扩展。我建议你签发证书时,把域名同时放在 CN 和 SAN 里,兼容旧系统。

3.8 公钥信息

这是证书的核心——包含证书持有者的公钥和算法信息。结构如下:

SubjectPublicKeyInfo ::= SEQUENCE {
    algorithm AlgorithmIdentifier,
    subjectPublicKey BIT STRING
}

算法标识符告诉你怎么解析公钥,公钥本身是一个比特串。常见的算法有:

算法 密钥长度 公钥大小 推荐场景
RSA 2048/4096 256/512 字节 兼容性要求高
ECDSA P-256/P-384 64/96 字节 性能优先
Ed25519 256 位 32 字节 极致性能

我个人偏好 ECDSA P-384。为什么?P-256 虽然够用,但 NSA 和 NIST 对 P-384 的安全余量评价更高。Ed25519 性能最好,但有些老旧系统不支持。

3.9 扩展项

扩展项是 v3 证书最强大的特性。它让证书不再只是一个「公钥+身份」的绑定,而是可以携带各种策略和约束信息。

常见的扩展项:

扩展项 OID 作用 关键/非关键
Basic Constraints 2.5.29.19 标记是否为 CA 证书,以及路径长度 关键
Key Usage 2.5.29.15 限制密钥用途(签名、加密等) 关键
Extended Key Usage 2.5.29.37 限制证书用途(服务器认证、客户端认证等) 非关键
Subject Alternative Name 2.5.29.17 列出证书适用的域名/IP 非关键
CRL Distribution Points 2.5.29.31 告诉你去哪里下载 CRL 非关键
Authority Information Access 1.3.6.1.5.5.7.1.1 提供 OCSP 响应器地址和 CA 证书下载地址 非关键
关键 vs 非关键: 如果解析器遇到一个标记为「关键」的扩展项但不认识它,必须拒绝这张证书。标记为「非关键」的扩展项,不认识可以跳过。所以,添加自定义扩展项时,除非你确定所有验证方都支持,否则别标记为关键。

我记得有一次帮客户排查证书问题,发现他们的中间证书没有设置 Basic Constraints 扩展。结果浏览器认为这张证书不是 CA 证书,拒绝用它去验证下级证书。这就是典型的「忘了标记自己是 CA」的错误。

3.10 小结

X.509 证书的每个字段都不是随便定义的。版本号决定了能力,序列号保证了唯一性,签名算法和签名值保证了完整性,颁发者和主体建立了信任关系,有效期控制了风险,公钥信息承载了加密能力,扩展项则让证书变得灵活而强大。