3. CRL 详解:从格式到发布,手把手拆解
CRL 这东西,说白了就是 CA 发的一张「黑名单」。谁家的证书被吊销了,序列号、时间、原因,全记在上面。我刚开始接触 PKI 的时候,总觉得 CRL 就是个简单的列表,后来被生产环境狠狠教育了一回——嗯,细节远比你想的多。
3.1 CRL 格式解析:别被 ASN.1 吓到
CRL 的底层是 ASN.1 编码,看着一堆花括号头疼。但你把它拆成三个部分,就好理解了。
CRL 核心三要素:
- TBSCertList —— 真正的吊销数据
- SignatureAlgorithm —— CA 签名用的算法
- SignatureValue —— CA 的数字签名
TBSCertList 里又套了好几层。我挑几个关键字段说说。
3.1.1 CRL 分发点(CRL Distribution Point)
这个字段告诉客户端:「你去哪儿下载 CRL」。格式长这样:
CRLDistributionPoints ::= SEQUENCE OF DistributionPoint
DistributionPoint ::= SEQUENCE {
distributionPoint [0] DistributionPointName OPTIONAL,
reasons [1] ReasonFlags OPTIONAL,
cRLIssuer [2] GeneralNames OPTIONAL
}
DistributionPointName ::= CHOICE {
fullName [0] GeneralNames,
nameRelativeToCRLIssuer [1] RelativeDistinguishedName
}
实际证书里最常见的是 fullName,指向一个 HTTP 或 LDAP 地址。比如:
URI:http://crl.example.com/ca.crl
我在项目中遇到过一个问题:客户端只支持 HTTP 下载,但 CA 配的是 LDAP。结果吊销检查全失败,业务直接瘫痪。所以,分发点的协议兼容性,上线前一定要测。
3.1.2 有效期(This Update / Next Update)
CRL 有两个时间戳:
| 字段 | 含义 | 典型值 |
|---|---|---|
| thisUpdate | 本次 CRL 的签发时间 | 2025-03-15T08:00:00Z |
| nextUpdate | 下次 CRL 更新的截止时间 | 2025-03-22T08:00:00Z |
客户端拿到 CRL 后,会检查 thisUpdate 和 nextUpdate。如果当前时间超过了 nextUpdate,客户端会认为 CRL 已过期,重新去下载新的。
我的习惯:生产环境里,我会把 CRL 的更新周期设为 24 小时,但 nextUpdate 留 48 小时的缓冲。万一 CA 宕机,客户端还能用旧的 CRL 撑一天。
3.1.3 吊销序列号(Revoked Certificates)
这是 CRL 的主体——一个序列号列表。每个条目包含:
- 用户证书序列号(唯一标识)
- 吊销日期
- 吊销原因码(可选)
原因码定义在 RFC 5280 里:
| 原因码 | 含义 | 常见场景 |
|---|---|---|
| 0 | unspecified | 未指定原因 |
| 1 | keyCompromise | 私钥泄露 |
| 2 | cACompromise | CA 私钥泄露 |
| 3 | affiliationChanged | 员工离职/部门变更 |
| 4 | superseded | 证书被新证书替代 |
| 5 | cessationOfOperation | 业务停止 |
| 6 | certificateHold | 临时挂起 |
避坑指南:我曾经遇到一个案例,CA 把所有吊销证书的原因码都填了 0(unspecified)。结果审计时被问「到底是不是私钥泄露?」——说不清楚。建议:能填具体原因就填具体,别偷懒。
3.2 CRL 生成与发布流程
流程不复杂,但每一步都有坑。我画了张图,你先看整体逻辑。
3.2.1 触发条件
CRL 不是实时生成的。你想想看,如果每吊销一张证书就生成一次 CRL,CA 得累死。实际生产中有三种触发方式:
- 定时生成:最常见。比如每天凌晨 2 点生成一次。
- 手动触发:紧急情况,比如发现私钥泄露,管理员手动跑一次。
- 阈值触发:吊销证书数量达到一定值(比如 1000 张),自动生成。
我建议:定时 + 阈值双保险。日常靠定时,紧急情况靠阈值。我在一个金融项目里就是这么配的,效果很好。
3.2.2 收集与构建
CA 从数据库里捞出所有「已吊销但未发布」的证书记录。然后组装成 TBSCertList。这一步要注意:
- 序列号必须唯一,不能重复
- 吊销时间用 UTC 格式,别用本地时间
- 原因码尽量填具体值
3.2.3 CA 签名
构建完 TBSCertList,CA 用自己的私钥给它签名。签名算法必须和 CA 证书里的签名算法一致。比如 CA 证书用的是 SHA256WithRSA,那 CRL 签名也得用这个。
我曾经踩过的坑:有一次升级 CA 软件,签名算法从 SHA1 换成了 SHA256。但 CRL 生成脚本没改,还是用 SHA1 签名。结果客户端验证签名失败,全部拒绝这个 CRL。所以,签名算法变更时,一定要同步更新 CRL 生成逻辑。
3.2.4 发布到分发点
签名完的 CRL 文件(.crl 或 .der 格式),推送到分发点。分发点可以是:
| 分发方式 | 协议 | 典型路径 |
|---|---|---|
| HTTP/HTTPS | HTTP GET | http://crl.example.com/ca.crl |
| LDAP | LDAP Search | ldap://ldap.example.com/cn=CA,dc=example,dc=com |
| 文件共享 | SCP/SFTP | 内网共享目录 |
发布完成后,记得更新 thisUpdate 和 nextUpdate。客户端下次来拉取时,就能拿到最新的 CRL。
3.3 验证 CRL 的完整性
CRL 发布后,别急着走。我习惯做三步验证:
- 签名验证:用 CA 公钥验签,确保 CRL 没被篡改。
- 有效期检查:
thisUpdate不能晚于当前时间,nextUpdate不能早于当前时间。 - 序列号抽样:随机挑几个吊销的证书序列号,确认在 CRL 里能找到。
一个小技巧:用 OpenSSL 命令行就能快速验证:
openssl crl -in ca.crl -text -noout
openssl crl -in ca.crl -CAfile ca.crt -verify
第一条看内容,第二条验签名。我每次发布完都会跑一遍。