OCSP详解:请求/响应结构、OCSP Responder 与在线查询过程

说实话,CRL 那套下载列表查状态的方式,虽然实现简单,但用起来总有点“笨重”。你想想看,每次都得拉一整个文件下来,哪怕你只关心一张证书。所以就有了 OCSP(在线证书状态协议),说白了就是“按需查询”。

我个人非常喜欢 OCSP 的设计思路——它把查状态这件事,从“批量下载”变成了“单次问询”。客户端发个请求,服务端回答“好/坏/未知”,干净利落。那它具体是怎么工作的?我们从最底层的报文结构开始聊。

OCSP 请求结构

OCSP 请求的核心目标就是一个:告诉服务端“帮我查查这张证书现在啥状态”。它不需要你发整张证书过去,只需要几个关键标识就够了。

一个典型的 OCSP 请求,主要包含两部分:

  • 协议版本:目前基本都是 v1(RFC 6960)。
  • CertID:这是核心,用来唯一标识被查询的证书。它由三部分构成:
    • 签发者公钥的 Hash(通过证书的 issuerPublicKey 计算 SHA-1 或 SHA-256)。
    • 签发者名称的 Hash(证书的 issuerDN)。
    • 被查询证书的序列号。这个最直接,因为序列号在同一个 CA 域内是唯一的。
我的一点经验:有人会问,为什么不直接发证书序列号?非得加上签发者Hash?这是为了防止不同CA之间出现序列号冲突。我见过一个案例,两个不同CA误签了相同序列号的证书,如果不加签发者标识,OCSP响应器就查错了。

另外,OCSP 请求里还可以带上一个可选的 Nonce(随机数),用来防止重放攻击。这个后面我们会聊到。

OCSP 响应结构

服务端收到请求后,会返回一个响应。响应的核心就是那张证书的状态。状态只有三种:

状态值 含义 说明
good 证书有效 未吊销,且在有效期内
revoked 证书已吊销 会附带吊销时间和原因
unknown 未知状态 找不到该证书信息

除了状态本身,响应里还有几个关键字段:

  • thisUpdate:这份响应的生成时间。
  • nextUpdate:建议下一次查询的时间。客户端可以在此之前缓存这个结果,不用重复查。
  • 签名:整个响应必须由 CA 或受信任的 OCSP 响应器签名。客户端需要验证这个签名,否则任何人都可以伪造一个“good”响应来骗你。
坑! 我曾经遇到过一个问题:一个客户端的 OCSP 缓存时间写死了 24 小时,但 CA 的响应里 nextUpdate 设置的只有 4 小时。结果这 24 小时里,证书被吊销了,客户端还傻乎乎地认为它有效。记住,nextUpdate 是建议,但客户端应该以更短的那个为准。

OCSP Responder 的角色

OCSP Responder 就是那个回答你“在不在”的服务端。它可以是 CA 自己部署的,也可以是第三方运营的。

Responder 的核心任务就一件事:查数据库或缓存,然后返回签名结果。但它面临一个很现实的问题——性能。

你想想看,一个大的 CA 可能每天要处理几百万次 OCSP 查询。每次查询都去硬盘里翻数据库,响应时间肯定撑不住。所以 OCSP Responder 在架构上通常这么玩:

  • 内存缓存:将最近生成的响应缓存起来。因为同一张证书可能被反复查询,缓存命中率很高。
  • 预签名:对有效期内的证书提前生成签名好的响应。查询时直接返回预签名结果,不需要实时签名,性能会好很多。
  • 负载均衡:通过多个节点分摊查询压力。
避坑指南:预签名虽然快,但有个隐患——万一证书在这段时间内被吊销了怎么办?所以预签名的时间窗口不能太长,我一般建议在 5-10 分钟。同时,证书吊销时,必须能够立即刷新预签名缓存。

在线查询过程总结

我把整个 OCSP 在线查询的过程归纳成下面这张图,你一看就明白了。

OCSP 在线查询过程 客户端 OCSP 请求 (含CertID+Nonce) OCSP 响应器 查缓存/数据库 验证请求有效性 good (有效) revoked (已吊销) unknown (未知) 客户端验证签名

我来把这张图的逻辑走一遍:

  1. 客户端构造请求:从证书中提取签发者Hash和序列号,拼接成一个CertID。如果需要防重放,再加一个Nonce。
  2. 发送给OCSP响应器:通过HTTP POST或GET方式把请求发过去(常见的是POST,因为请求体可以大一些)。
  3. 响应器处理:先查自己的缓存或数据库,找到这张证书对应的状态。同时验证请求格式和Nonce是否匹配。
  4. 返回签名响应:根据状态返回good/revoked/unknown,并签名整个响应。
  5. 客户端验证:收到响应后,第一件事就是验签名。如果签名不对,直接拒绝。然后检查Nonce是否一致,防止重放。最后看一眼thisUpdate和nextUpdate,决定要不要缓存这个结果。
我个人觉得,OCSP 最巧妙的地方就在于它把“查状态”这件事拆成了“问”和“答”两个动作,而且每个动作都考虑到了安全性和性能。不过,它也不是万能的,最大的问题就是——如果客户端连不上OCSP响应器怎么办?这种“软失败”策略很容易被攻击者利用。这也是为什么后来有了OCSP Stapling(证书状态钉扎),把“问”这个动作交给了服务端,而不是客户端。