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的完整结构画出来了。我建议你保存下来,以后排查问题时对照着看。
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”前缀。你在做自动化脚本比对时,要注意统一格式。我曾经因为大小写问题,导致脚本误判吊销状态。