OCSP 协议基础:理解响应结构

刚接触 OCSP 的兄弟,常会问:它到底返回了什么?说白了,OCSP 响应就是一个标准的数据信封,告诉你某个证书当前是否有效。我当年第一次看 RFC 6960 时也被各种 ASN.1 绕晕了,后来调试了几次异常才彻底搞明白。咱们今天就把这个信封拆开,看看里面到底装了啥。

OCSP 响应骨架

一个完整的 OCSP 响应,由三部分组成:

  • TBSResponseData —— 响应的正文,包含证书状态、时间、扩展等
  • 签名算法标识 —— 告诉你怎么验证签名
  • 签名值 —— 防止伪造的保证

嗯,这里要注意:不是所有响应都有签名。OCSP 允许在特定条件下返回无签名的“委托响应”,但生产环境中几乎见不到。我建议你一律按有签名对待,别给自己留坑。

核心要点:OCSP 响应是一段 DER 编码的 ASN.1 数据,最外层是 BasicOCSPResponse,它把上面三部分打包在一起。

TBSResponseData 内部结构

这是响应的核心,它决定了 OCSP 到底“说了什么”。它的字段如下:

字段含义我的踩坑点
version协议版本,当前只有0(v1)曾经遇到过一个老旧CA返回了v2,直接导致客户端解析崩溃
responderID响应者身份(byName 或 byKey)byKey 方式更常见,但有些库只支持 byName
producedAt响应生成时间(UTC)时区问题坑过很多人,我习惯统一转成UTC+0再比较
responses一个或多个单证书状态对象批量查询时,顺序可能和请求不一致,别想当然
responseExtensions扩展字段,如 nonce、archive cutoffnonce 不一致会导致客户端拒绝响应

你想想看,如果 producedAt 比当前时间还晚,那这个响应就是未来数据,不能信。我曾经排查过一个诡异问题,CA 服务器时钟跑偏了,所有响应的时间戳都早跳了 5 分钟,客户端直接全部拒绝。

单证书状态对象

每个 SingleResponse 包含了对应证书的查询结果:

  • certID —— 用颁发者哈希 + 公钥哈希 + 序列号唯一标识证书
  • certStatus —— 三个值:good、revoked、unknown
  • thisUpdate —— 本状态有效的起始时间
  • nextUpdate —— 建议下次查询的时间(可选,但强烈建议提供)
  • singleExtensions —— 单个证书的扩展

技巧:当 nextUpdate 缺失时,客户端必须频繁轮询,否则风险敞口很大。我建议在生产中要求 CA 必须返回 nextUpdate,至少不要超过 4 小时。

revoked 状态的附加信息

如果状态是 revoked,OCSP 必须返回吊销时间和原因码。原因码是个枚举值:

unused             (0)
keyCompromise      (1)
cACompromise       (2)
affiliationChanged (3)
superseded         (4)
cessationOfOperation (5)
certificateHold    (6)
removeFromCRL      (8)
privilegeWithdrawn (9)
aACompromise       (10)

实践中,最常见的是 keyCompromise 和 superseded。我遇到过被吊销的原因码填了 0(unused),导致一些旧版库无法识别而报错。嗯,这个坑在 RFC 5280 里其实有说明,但实现者经常忽视。

响应签名机制

签名的作用是让你确认这条响应确实来自受信的 OCSP 响应器。验证流程大致是:

  1. 提取 TBSResponseData 的原始字节
  2. 用签名算法标识找到对应的公钥(通常是 CA 证书的 OCSP 扩展或专门响应器证书)
  3. 用该公钥验证签名值是否匹配
  4. 确认响应器证书本身有效且未被撤销

警告:很多实现的 bug 出在第三步 —— 只验证签名算法,却不验证响应器证书的信任链。我曾经在审计中见过一个系统,直接信任了任何能解开签名的证书,结果被中间人攻击了。

OCSP 响应的整体处理流程

下面这张图展示了从接收到 OCSP 响应到最终判定证书状态的全过程:

OCSP 响应处理流程 OCSP 响应处理流程 接收 DER 编码数据 解析 BasicOCSPResponse 提取 TBSResponseData + 签名字段 验证签名 + 响应器证书 遍历 SingleResponse 列表 判断 certStatus 并记录结果 输出 good / revoked / unknown 说明:每一步失败都应返回错误码,而不是默认 unknown

常见异常与排查思路

理解响应结构后,我们来看最容易出错的几个点:

  • 响应太大,HTTP 传输截断 —— 批量查询时响应尺寸可能超过几十 KB,HTTP 流未正确读完就会解析失败
  • ASN.1 编码错误 —— 某些 CA 实现有缺陷,比如乱填长度字段,导致解析器报错
  • 签名验证失败 —— 原因可能是响应器证书过期、信任链断裂、签名算法不匹配
  • nonce 不匹配 —— 请求中带了 nonce,但响应没有回显,或回显了不同的值
  • 版本字段异常 —— 按理应该为 0,但有的 CA 填了 1 甚至 255

提示:当你怀疑响应本身有问题时,第一步就是用 openssl ocsp -resp_text -respin response.der 打印出文本形式。我几乎每次排查异常都先走这一步,能快速定位是哪个字段出问题。

小结一下

OCSP 响应结构,说白了就是一个带上签名的数据包,里面包含了证书的状态、时间戳、可选扩展。理解 TBSResponseData、签名、SingleResponse 这三层,你就掌握了排查异常的最基本武器。下次遇到 OCSP 报错,先拆包,再分析,别盲目改配置。