2、CRL结构解析:字段详解与版本号

好,咱们今天来拆一个真正的CRL。你想想看,一个证书被吊销了,CA怎么通知全世界?就是靠CRL。这个文件的结构,说白了就是一张“黑名单”,但它的格式非常严谨。我刚开始接触PKI时,总觉得CRL就是个列表,后来踩了坑才发现,每个字段都有它的设计意图。

2.1 CRL的顶层结构

一个标准的CRL,在ASN.1描述里长这样。别被吓到,我们只看核心字段:

CertificateList  ::=  SEQUENCE  {
    tbsCertList          TBSCertList,
    signatureAlgorithm   AlgorithmIdentifier,
    signatureValue       BIT STRING
}

嗯,这里要注意,CRL分三块:待签数据体(tbsCertList)、签名算法、签名值。跟证书的结构几乎一样,只是内容不同。我个人习惯把tbsCertList称为“肉”,后面两个是“皮”。验签时,CA用公钥解开签名值,跟tbsCertList的摘要比对,就能确认这个CRL没被篡改。

核心理解:CRL的签名机制保证了吊销列表的完整性和权威性。任何中间人篡改吊销条目,验签都会失败。

2.2 颁发者(Issuer)

颁发者字段,就是签发这张CRL的CA名称。它必须跟签发证书的Issuer字段完全一致。我在项目中遇到过一个问题:某个子CA的CRL颁发者DN里多了一个空格,结果客户端验签时匹配不上父CA的证书,直接报错。这种坑,排查起来特别费劲。

颁发者通常是一个X.500 Distinguished Name,比如:

CN=My Root CA, O=My Company, C=CN

注意,如果CA使用Name Constraints,这里的颁发者必须严格匹配。

2.3 本次更新(This Update)与下次更新(Next Update)

这两个字段定义了CRL的有效期窗口。

  • This Update:CRL的签发时间。客户端拿到CRL后,会检查这个时间是否在合理范围内。
  • Next Update:CA承诺下一次发布CRL的时间。客户端在这个时间之前,可以缓存当前CRL;超过这个时间,就必须去拉取新的CRL。

我曾经见过一个生产事故:某CA的Next Update设置成了10年后,结果中间有证书被吊销,客户端因为CRL还没过期,一直用旧列表,导致吊销的证书被继续信任。所以,Next Update不能设得太长,一般建议24小时以内。

避坑指南:我曾经在配置CRL时,把Next Update写成了UTC时间但忘记加“Z”,导致某些客户端解析失败。记住,时间格式必须用UTCTime(YYMMDDHHMMSSZ)或GeneralizedTime(YYYYMMDDHHMMSSZ),且必须带时区标识。

2.4 吊销证书列表(Revoked Certificates)

这是CRL的核心——真正的黑名单。每个条目包含:

字段 说明 我的经验
用户证书序列号 被吊销证书的唯一序列号 必须跟证书里的序列号完全一致,大小写敏感
吊销日期 CA决定吊销的时间 不是证书过期时间,是吊销动作发生的时间
CRL条目扩展 可选,比如吊销原因码 我建议至少带上原因码,方便审计

吊销原因码(CRLReason)常见值:

  • 0x00 – 未指定
  • 0x01 – 密钥泄露
  • 0x02 – CA泄露
  • 0x03 – 从属关系变更
  • 0x04 – 证书被取代
  • 0x05 – 操作停止
  • 0x06 – 证书暂停(用于CRL的增量更新)

你想想看,如果CRL里只有序列号没有原因码,审计时根本不知道为啥吊销。我个人习惯在生产环境强制要求填写原因码。

2.5 CRL版本号与签名算法

CRL有两个版本:

  • v1(版本0):没有扩展字段,功能有限。现在基本不用了。
  • v2(版本1):支持CRL扩展和条目扩展。几乎所有现代PKI都用v2。

签名算法字段,跟证书里的签名算法一样,指定了CA用什么算法对tbsCertList签名。常见的有:

  • sha256WithRSAEncryption(OID: 1.2.840.113549.1.1.11)
  • ecdsa-with-SHA256(OID: 1.2.840.10045.4.3.2)
  • sha256WithRSASignature(OID: 1.2.840.113549.1.1.10,注意这是RSASSA-PSS)

嗯,这里要注意,签名算法必须跟CA证书里的Subject Public Key Info匹配。比如CA证书是RSA密钥,就不能用ECDSA签名。我遇到过有人配置错了,结果CRL验签一直失败。

小技巧:用OpenSSL查看CRL时,可以用openssl crl -in crl.pem -text -noout。你会看到版本号显示为“v2”,签名算法显示为“sha256WithRSAEncryption”。如果版本号是“v1”,说明这个CRL不支持扩展,功能受限。

2.6 知识体系图:CRL结构全景

下面这张图,把CRL的完整结构画出来了。我建议你保存下来,以后排查问题时对照着看。

CRL结构全景图 CRL结构全景图 CertificateList tbsCertList (待签数据体) signatureAlgorithm signatureValue (签名值) 版本号 (Version) — v1 或 v2 签名算法 (Signature) 颁发者 (Issuer) 本次更新 (This Update) 下次更新 (Next Update) 吊销证书列表 (RevokedCertificates) CRL扩展 (Extensions) — v2特有 吊销条目1: 序列号 | 吊销日期 | 原因码 吊销条目2: 序列号 | 吊销日期 | 原因码 吊销条目3: 序列号 | 吊销日期 | 原因码 ... (最多可达数千条) 💡 关键点 CRL的签名算法必须与CA证书密钥类型匹配 Next Update不能设置过长,建议≤24小时

2.7 实战:用OpenSSL解析一个CRL

光说不练假把式。我们拿一个真实的CRL来解剖。假设你有一个CRL文件叫ca.crl:

openssl crl -in ca.crl -text -noout

输出大概长这样:

Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = My Root CA, O = My Company, C = CN
        Last Update: Apr 10 12:00:00 2025 GMT
        Next Update: Apr 11 12:00:00 2025 GMT
        CRL extensions:
            X509v3 Authority Key Identifier: keyid:AB:CD:...
            X509v3 CRL Number: 1234
Revoked Certificates:
    Serial Number: 0A1B2C3D4E5F
        Revocation Date: Apr  8 10:30:00 2025 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code: Key Compromise
    Serial Number: 0F1E2D3C4B5A
        Revocation Date: Apr  9 14:00:00 2025 GMT
        CRL entry extensions:
            X509v3 CRL Reason Code: Superseded

你看,版本号显示为v2,签名算法是sha256WithRSAEncryption。颁发者、本次更新、下次更新一目了然。吊销列表里,每个条目都有序列号和吊销日期,还带上了原因码。我个人习惯在CRL扩展里加上CRL Number,方便客户端判断CRL的新旧。

重要提醒:CRL的序列号是十六进制表示的,但OpenSSL输出时可能不带“0x”前缀。你在做自动化脚本比对时,要注意统一格式。我曾经因为大小写问题,导致脚本误判吊销状态。