第三章:常见OCSP响应状态码及含义
OCSP 响应到底能返回多少种状态?说破天其实就三种:good、revoked、unknown。我在项目里见过不少人以为 OCSP 只会返回“有效”或“无效”,结果被“unknown”坑得头皮发麻。今天咱们就把这三种状态掰开揉碎,顺便聊聊那些不常见但一出现就让人头疼的扩展状态。
3.1 三大主状态码
OCSP 响应体里的 certStatus 字段就这三个值,RFC 6960 写得明明白白。咱们直接上表对照。
| 状态码 | 枚举值 | 含义 |
|---|---|---|
| good | 0 | 证书未被吊销,当前有效 |
| revoked | 1 | 证书已被吊销,必须拒绝 |
| unknown | 2 | OCSP 响应器不知道此证书的状态 |
等等,有人可能会问:“为什么没有 expired 或 not yet valid?” 记住,OCSP 只管“吊销状态”,证书的有效期是客户端自己拿本地时间对比的。OCSP 响应里不会告诉你证书过期了,你收到 good 不代表证书现在还不过期——这事我踩过坑,后面会讲。
3.2 深入理解 good 状态
good 不是简单的“证书没问题”。它表示:在当前响应时间点上,CA 确认该证书没有被吊销。但 good 里藏着两个容易忽略的点:
- 响应时间戳:
thisUpdate和nextUpdate决定了 good 的有效窗口。如果客户端系统时间不对,可能会把一张仍有效的 good 响应当成过期的。 - 签名校验:good 响应本身必须由 CA 或授权响应器签名。我曾经遇到过一个案例,客户端收到 good,但没校验签名,结果实际上是中间人伪造的响应——那证书本身早就被吊销了。
避坑指南
我习惯在收到 good 后多做一个步骤:检查 thisUpdate 是否在证书有效期内。有的 CA 会发布 good 响应,但此时证书已经过期了——虽然这种情况少见,但安全加固总没错。
3.3 revoked 状态——直接拉红
revoked 是唯一需要客户端立即拒绝证书的状态。响应里会附带两个关键字段:revocationTime 和可选的 revocationReason。
吊销原因码(CRLReason 枚举)
- unspecified (0):未指定原因,一般 CA 系统异常导致。
- keyCompromise (1):私钥泄露,最常见,也是最严重的。
- cACompromise (2):CA 私钥泄露,整个 CA 都可能被撤销信任。
- affiliationChanged (3):证书持有者所属关系变更。
- superseded (4):证书被替代,比如重新签发。
- cessationOfOperation (5):证书实体停止运营。
- certificateHold (6):暂挂,后续可能解除。
- removeFromCRL (8):仅用于 delta CRL,OCSP 里极少见。
- privilegeWithdrawn (9):权限被撤销。
- aACompromise (10):AA属性机构私钥泄露。
嗯,这里要特别注意的是 certificateHold (6)。我见过一个开发同学把暂挂当成永久吊销处理,导致用户证书被锁死一整周。实际上,如果 CA 后续解除了暂挂,OCSP 响应会从 revoked 变成 good。所以客户端在遇到 revoked(但原因是 6)时,可以考虑间隔一段时间再查询一次,或者提醒管理员手动确认。
3.4 unknown 状态——最头疼的“不知道”
unknown 意味着 OCSP 响应器无法确认证书状态。为什么会这样?我总结了几种常见场景:
- 序列号没录入:CA 颁发了证书,但还没来得及同步到 OCSP 数据库。尤其 batch 签发场景,数据延迟可达数分钟到数小时。
- CA 域名不匹配:查询时指定了错误的
issuerNameHash或issuerKeyHash,响应器找不到对应 CA。 - OCSP 响应器配置问题:CA 把 OCSP 指向了错误的 URI,或者响应器只负责部分分区。
- 网络路径被篡改:攻击者返回伪造的 unknown 来绕过吊销检查(虽然概率低,但不可忽略)。
注意:收到 unknown 时,客户端策略必须是“硬失败”还是“软失败”?很多浏览器选择了软失败(即允许连接),因为 unknown 可能只是临时故障。但如果你做的是金融或高安全场景,我建议配置为“硬失败”——宁可拒绝一个可能有效的证书,也不要信任一个可能被吊销的。具体策略要结合业务容忍度。
3.5 响应状态字节(Response Status)
除了 certStatus,OCSP 响应外层还有一个 responseStatus 字节,表示协议层面的结果。别搞混了,这两个是不同的层级。
| 状态字节 | 含义 | 典型场景 |
|---|---|---|
| successful (0) | 协议正常,响应体中有实际状态 | 最常见 |
| malformedRequest (1) | 请求格式错误 | 编码问题、缺少哈希字段 |
| internalError (2) | OCSP 响应器内部异常 | 数据库失联、配置错误 |
| tryLater (3) | 请稍后重试 | 系统过载、维护中 |
| sigRequired (5) | 请求需要签名(未实现) | 旧版本 RFC 遗留值,很少用 |
| unauthorized (6) | 响应器无权回答该请求 | 身份验证失败、请求了错误 CA |
我个人习惯:先检查 responseStatus 是否为 0,如果不是,直接抛异常,不要再解析 certStatus。否则你会拿到一堆空指针。
3.6 一张图理清核心逻辑
3.7 实战排查小技巧
讲完理论,说点能直接用的。当你发现 OCSP 响应异常时,按这个顺序查:
- 抓包看 responseStatus:如果是 2 (internalError),大概率是 CA 服务端问题。我曾经遇到一个案例,数据库连接池耗尽,OCSP 连续返回 2 长达 20 分钟。
- 如果 responseStatus 是 0 但 certStatus 是 unknown:先确认请求里
issuerNameHash和issuerKeyHash是否计算正确。拿 openssl 算一下,对比 CA 证书的哈希值。 - 用 openssl 命令行测试:
# 查询证书状态
openssl ocsp -issuer ca.pem -cert test.pem \
-url http://ocsp.example.com \
-resp_text -no_nonce
# 如果返回 unknown,加上 -CAfile 指定信任的 CA
openssl ocsp -issuer ca.pem -cert test.pem \
-url http://ocsp.example.com \
-CAfile root.pem -resp_text
我的个人习惯
在做 OCSP 客户端开发时,我会打印完整的响应 DER 十六进制,然后逐字段解析比对 RFC 文档。虽然费时,但能帮你发现一些库隐藏的错误——比如有的库把 responseStatus 和 certStatus 的顺序搞反了。
3.8 本章小结
三种核心状态码 good、revoked、unknown,加上外层的 responseStatus,构成了 OCSP 响应的全部骨架。别小看 unknown,它在生产环境里出现频率不低,处理策略选错了可能会引发大规模故障。本章的内容足够你日常排查 90% 的 OCSP 异常,剩下的 10% 通常是协议层实现的 bug,需要在代码级深挖。好,咱们就到这儿。