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 生成与发布流程

流程不复杂,但每一步都有坑。我画了张图,你先看整体逻辑。

CRL 生成与发布流程 CRL 生成与发布流程 触发条件 收集吊销列表 构建 TBSCertList CA 签名 发布到分发点 触发条件示例 • 定时任务(每24h) • 手动触发(紧急吊销) • 证书数量达到阈值 收集内容 • 序列号 + 吊销时间 • 吊销原因码 签名关键点 • 使用 CA 私钥签名 • 签名算法与证书一致

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 发布后,别急着走。我习惯做三步验证:

  1. 签名验证:用 CA 公钥验签,确保 CRL 没被篡改。
  2. 有效期检查:thisUpdate 不能晚于当前时间,nextUpdate 不能早于当前时间。
  3. 序列号抽样:随机挑几个吊销的证书序列号,确认在 CRL 里能找到。

一个小技巧:用 OpenSSL 命令行就能快速验证:

openssl crl -in ca.crl -text -noout
openssl crl -in ca.crl -CAfile ca.crt -verify

第一条看内容,第二条验签名。我每次发布完都会跑一遍。