第三章:常见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 响应器无法确认证书状态。为什么会这样?我总结了几种常见场景:

  1. 序列号没录入:CA 颁发了证书,但还没来得及同步到 OCSP 数据库。尤其 batch 签发场景,数据延迟可达数分钟到数小时。
  2. CA 域名不匹配:查询时指定了错误的 issuerNameHash 或 issuerKeyHash,响应器找不到对应 CA。
  3. OCSP 响应器配置问题:CA 把 OCSP 指向了错误的 URI,或者响应器只负责部分分区。
  4. 网络路径被篡改:攻击者返回伪造的 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 一张图理清核心逻辑

OCSP 响应状态码核心流程 收到 OCSP 响应 responseStatus == 0? 是 否(1/2/3/5/6) 协议错误,拒绝请求 解析 certStatus good (0) 证书未被吊销 revoked (1) 立即拒绝 unknown (2) 查不到状态 依据业务策略处理 unknown 可软失败/硬失败 取决于安全级别

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,需要在代码级深挖。好,咱们就到这儿。