第1章 OCSP协议基础

大家好,我是这次课程的主讲。做性能瓶颈定位,根基是对协议本身的理解。就像修车,你得先知道发动机怎么转,才能听出异响在哪。

OCSP,全称在线证书状态协议。说白了,就是用来查数字证书有没有被吊销的。你访问一个HTTPS网站,浏览器拿到服务器证书后,得确认这张证书还是不是“有效”的。以前大家靠下载CRL(证书吊销列表),现在更多靠OCSP。为什么?效率问题。我后面会细说。

1.1 OCSP工作原理

OCSP的工作模式其实很直白:客户端问,服务端答。一次请求对应一次响应,没有复杂的握手,就是个简单的“查询-应答”模型。

具体流程是这样的:

  • 客户端发起请求:浏览器或应用服务器构造一个OCSP请求,里面塞入待查证书的序列号、颁发者信息等。然后发给CA(证书颁发机构)的OCSP响应器。
  • 响应器查询状态:OCSP响应器收到请求后,去后端数据库或缓存里查这个证书当前的吊销状态。查的是实时数据,不是几个小时前快照。
  • 返回响应:响应器把结果封装成响应报文,签名后发回客户端。结果只有三种:good(正常)、revoked(已吊销)、unknown(未知)。
  • 客户端验证:客户端拿到响应,先验签名,再读状态。如果是good,继续后续通信;如果是revoked或unknown,浏览器就会给你拦个大红页面。

嗯,这里要注意一个关键点——非阻塞。客户端发送OCSP请求后,一般会设置一个超时时间,比如5秒。如果响应器没在规定时间内回复,客户端怎么处理?这直接关系到系统能不能扛住突发的性能抖动。我后面讲日志定位时会反复提到这个超时设计。

核心要点:OCSP的核心就是“在线实时查询,签名防篡改”。你想想看,如果响应器挂了或者网络延迟,客户端的体验会怎样?

1.2 请求与响应格式

OCSP的数据格式用的是ASN.1,然后用DER编码。听起来很底层,但我们在日志里看到的一般是Base64编码后的文本,或者直接是报文字段。你不需要手写编码器,但得能读懂日志里那些字段的含义。

一次典型的OCSP请求结构如下:

OCSPRequest ::= SEQUENCE {
    tbsRequest      TBSRequest,
    optionalSignature [0] EXPLICIT Signature OPTIONAL
}

TBSRequest ::= SEQUENCE {
    version            [0] EXPLICIT Version DEFAULT v1,
    requestorName      [1] EXPLICIT GeneralName OPTIONAL,
    requestList        SEQUENCE OF Request,
    requestExtensions  [2] EXPLICIT Extensions OPTIONAL
}

Request ::= SEQUENCE {
    reqCert     CertID,
    singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL
}

CertID ::= SEQUENCE {
    hashAlgorithm   AlgorithmIdentifier,
    issuerNameHash  OCTET STRING,
    issuerKeyHash   OCTET STRING,
    serialNumber    CertificateSerialNumber
}

请求里最核心的是CertID,它用哈希值标识了你要查哪张证书。响应结构同样清晰:

OCSPResponse ::= SEQUENCE {
    responseStatus         OCSPResponseStatus,
    responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL
}

ResponseBytes ::= SEQUENCE {
    responseType   OBJECT IDENTIFIER,
    response       OCTET STRING
}

BasicOCSPResponse ::= SEQUENCE {
    tbsResponseData      ResponseData,
    signatureAlgorithm   AlgorithmIdentifier,
    signature            BIT STRING,
    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL
}

ResponseData ::= SEQUENCE {
    version              [0] EXPLICIT Version DEFAULT v1,
    responderID          ResponderID,
    producedAt           GeneralizedTime,
    responses            SEQUENCE OF SingleResponse,
    responseExtensions   [2] EXPLICIT Extensions OPTIONAL
}

对性能日志分析来说,你最需要关注几个字段:

  • responseStatus:响应状态码。0表示成功,其他值表示错误(比如1表示malformedRequest,2表示internalError)。这个字段直接告诉你是协议层面出了问题,还是业务层面。
  • producedAt:响应生成时间。这是计算延迟的关键指标。
  • signature:数字签名。验证签名会消耗CPU,这也是性能开销的来源之一。
  • certStatus:证书状态。good/revoked/unknown。注意,unknown不一定是系统故障,也可能是请求的证书不在CA管辖范围内。

个人经验:我在项目中遇到过一种情况,OCSP响应器返回了大量internalError,但后端数据库其实没问题。后来发现是响应器线程池被打满了,队列积压导致超时。你看,从日志里读到响应状态码,往往就是问题的第一线索。

1.3 与CRL的对比

CRL是OCSP出现之前的主流方案。CA定期发布一个列表文件,里面记录了所有被吊销的证书序列号。客户端得自己下载这个文件,然后比对。两者对比下来,差异很明显。

维度 CRL(证书吊销列表) OCSP(在线证书状态协议)
工作原理 客户端主动下载完整的列表文件 客户端逐张查询证书状态
实时性 取决于发布周期(通常以小时计) 实时查询,秒级内即可返回
带宽开销 列表可能很大,动辄几十MB 每次请求响应只有几百字节
客户端处理压力 需解析大文件、存储、更新 无存储压力,只做在线请求
隐私性 无法泄露用户的查询行为 CA能知道用户查了哪张证书
服务端压力 仅需提供静态文件下载 需高并发处理实时查询请求
安全风险 列表被篡改影响大,但可签名 需防范DDoS攻击和响应伪造

从表里能看出来,OCSP在实时性和带宽上完胜CRL,但开了服务端压力的副作用。我刚开始做证书验证时也纠结过这问题——选CRL还是OCSP?后来发现,大部分现代系统和浏览器都倾向OCSP,配合OCSP Stapling(服务端代为查询)来弥补隐私缺陷。

为什么要提CRL?因为在性能日志里,你可能看到客户端回退到CRL。比如OCSP响应器连续超时,客户端会启用“软失败”策略,改用CRL兜底。这种回退行为在日志里会有明确标记,也是性能瓶颈定位的重要线索。我曾经见过一个系统,OCSP响应器负载过高后,客户端全都切回CRL,本来做好的性能优化全白费了。

避坑指南:不要以为OCSP一定比CRL快。如果OCSP响应器离客户端很远(跨大洋),网络延迟可能让你白等。我曾经在项目中遇到过一个案例,客户抱怨证书验证慢,最后定位到是OCSP响应器部署在海外,国内客户端每次查询都要等800ms。这种情况下,CRL的本地缓存反而更快。选择哪个,得结合你的网络拓扑来权衡。

在这一章结束前,我用一张图把OCSP的核心知识串起来。你可以把它当作本章的知识索引。

OCSP协议知识框架 OCSP核心 工作原理 客户端发起请求 响应器查询状态 返回签名响应 请求/响应格式 ASN.1 + DER编码 关键字段解析 与CRL对比 CRL:下载列表 OCSP:在线查询 核心权衡:实时性 ↔ 服务端压力 ↔ 网络开销 日志分析就是在这三者间找瓶颈

总结:OCSP不是你想象中那种“打开即用”的协议。它的实时性带来了性能压力,它的格式解析带来了CPU开销,它的网络依赖带来了延迟风险。搞懂这些基础,你再去看那些日志里的“OCSP timeout”、“OCSP internalError”、“responseStatus: 2”时,就不会一头雾水了。