3. OCSP 机制详解:在线证书状态协议
CRL 有个硬伤——它是个“大列表”,越下越大。你想想看,一个 CA 签了几百万张证书,CRL 文件动不动就几十兆。客户端每次都要下载整个列表,就为了查一张证书的状态?太浪费了。
OCSP(Online Certificate Status Protocol)就是来解决这个问题的。它把“批量查询”变成了“单点查询”。你只问一张证书,它只回答这张证书。说白了,就是个轻量级的证书状态查询接口。
核心思想:OCSP 让客户端直接问 CA(或授权响应者):“这张证书现在有效吗?”CA 回答:“有效”、“已吊销”或“未知”。一问一答,干净利落。
3.1 OCSP 的工作原理
OCSP 的工作流程其实很简单。我画个图你就明白了:
具体步骤是这样的:
- 客户端发起请求——客户端拿到一张证书后,提取证书的颁发者信息和序列号,构造一个 OCSP 请求,发给 CA 的 OCSP 响应者地址(证书的 Authority Information Access 扩展里就有这个地址)。
- 响应者查询状态——OCSP 响应者收到请求后,查自己的数据库或缓存,确认这张证书当前的状态。
- 响应者返回结果——响应者把结果(good / revoked / unknown)打包成 OCSP 响应,用私钥签名,返回给客户端。
- 客户端验证响应——客户端拿到响应后,验证签名是否有效,确认响应确实来自可信的响应者。
我的经验:OCSP 响应者的地址通常写在证书的 AIA 扩展里。我遇到过好几次,证书里的 AIA 地址写的是内网 IP,结果客户端在外网根本连不上。所以签发证书时,一定要确认 AIA 地址是客户端能访问的。
3.2 OCSP 请求与响应格式
OCSP 用的是 ASN.1 编码,底层走 DER 或 BER。不过你不用手写 ASN.1,大部分语言都有现成的库。我带你看看核心结构就行。
请求格式
一个 OCSP 请求包含以下关键字段:
| 字段 | 说明 | 是否必填 |
|---|---|---|
| 版本 | 目前只有 v1(值为 0) | 是 |
| 请求列表 | 包含一个或多个证书标识(颁发者哈希 + 序列号) | 是 |
| 请求扩展 | 可选,比如 nonce 防重放 | 否 |
| 签名 | 可选,客户端签名(很少用) | 否 |
用 OpenSSL 模拟一个 OCSP 请求,大概长这样:
# 生成 OCSP 请求并发送
openssl ocsp -issuer ca.crt -cert server.crt -url http://ocsp.example.com -text
# 输出示例(简化)
OCSP Request Data:
Version: 1 (0x0)
Requestor List:
Certificate ID:
Hash Algorithm: sha1
Issuer Name Hash: 0x3A1B2C...
Issuer Key Hash: 0x4D5E6F...
Serial Number: 0x1234567890ABCDEF
Request Extensions:
Nonce: 0xDEADBEEF...
响应格式
OCSP 响应格式稍微复杂一点,但核心就三个状态:
| 状态码 | 含义 | 说明 |
|---|---|---|
| good | 证书有效 | 注意:good 不代表证书未被吊销,只代表“在响应者看来,当前状态正常” |
| revoked | 证书已吊销 | 响应中会包含吊销时间和吊销原因 |
| unknown | 未知状态 | 响应者不认识这张证书,或者查不到记录 |
响应里还包含一个关键字段——thisUpdate 和 nextUpdate。前者是响应生成时间,后者是建议的下次查询时间。客户端可以缓存响应,直到 nextUpdate 过期。
注意:OCSP 响应默认不包含证书链。客户端拿到响应后,需要自己想办法验证响应者的签名证书是否可信。如果响应者的证书本身已经过期或被吊销,那这个响应就是废纸一张。
3.3 OCSP Stapling 技术
OCSP 有个明显的性能问题——客户端每次建立 TLS 连接,都要额外发一个 HTTP 请求去查证书状态。这增加了握手延迟,也加重了 OCSP 响应者的负担。
OCSP Stapling 就是来解决这个问题的。它的思路很简单:让服务器替客户端去查。
具体流程是这样的:
- 服务器(比如 Nginx)定期向 OCSP 响应者查询自己证书的状态。
- 响应者返回一个带时间戳的 OCSP 响应。
- 服务器把这个响应“订”在 TLS 握手时的 Certificate 消息后面。
- 客户端收到证书的同时,也收到了 OCSP 响应,直接验证,不用再额外发请求。
我画个对比图,你一看就明白:
Nginx 开启 OCSP Stapling 的配置也很简单:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# 开启 OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
# 指定 CA 证书链(用于验证 OCSP 响应签名)
ssl_trusted_certificate /etc/nginx/certs/ca-chain.crt;
# OCSP 响应者地址(可选,默认从证书 AIA 提取)
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}
我踩过的坑:有一次配 OCSP Stapling,死活不生效。查了半天,发现是 ssl_trusted_certificate 指向的文件不完整,缺少中间 CA 证书。Nginx 拿不到完整的验证链,就把 Stapling 静默关闭了。记住:这个文件必须包含完整的 CA 链,不能只放根证书。
3.4 OCSP 的优缺点
聊了这么多,咱们来总结一下 OCSP 的优缺点。我在实际项目中用过好几年,体会很深。
优点
- 实时性好——每次查询都是实时的,不像 CRL 有更新延迟。证书刚被吊销,OCSP 响应者马上就能返回 revoked。
- 流量小——只查一张证书,响应体很小,通常几百字节。不像 CRL 动不动就几兆甚至几十兆。
- 隐私性好(相对 CRL)——客户端只告诉响应者“我在查这张证书”,而不是暴露整个证书列表。不过严格来说,OCSP 还是有隐私问题,因为响应者知道你正在访问哪个网站。
- 支持精细化状态——可以返回吊销原因、吊销时间等详细信息。
缺点
- 额外延迟——每次 TLS 握手都要多一次 HTTP 请求。虽然 Stapling 解决了这个问题,但并不是所有服务器都支持。
- 单点故障——OCSP 响应者挂了,客户端就查不到状态。很多客户端会“软失效”(允许连接),这就等于吊销机制失效了。
- 隐私问题——OCSP 响应者知道你在查哪张证书,也就知道你正在访问哪个网站。虽然比 CRL 好一点,但远谈不上完美。
- 依赖网络——客户端必须能访问 OCSP 响应者。内网环境、离线环境、网络隔离环境,OCSP 基本用不了。
我的建议:生产环境最好把 OCSP Stapling 和 CRL 结合起来用。Stapling 解决性能问题,CRL 作为兜底。如果客户端既收不到 Stapling 响应,又下载不到 CRL,那就要根据安全策略决定是允许还是拒绝连接。
3.5 避坑指南
最后分享几个我踩过的坑,希望能帮你少走弯路:
- OCSP 响应者地址写错了——证书里的 AIA 地址一定要测试。我见过有人把测试环境的地址写到了生产证书里,结果客户端全连不上。
- Stapling 静默失效——Nginx 的 Stapling 如果配置不对,不会报错,只是默默关闭。一定要用
openssl s_client -status验证。 - OCSP 响应缓存时间——nextUpdate 字段决定了缓存时长。设得太短,响应者压力大;设得太长,吊销响应不及时。我个人习惯设 4-6 小时。
- nonce 问题——有些 OCSP 响应者不支持 nonce 扩展。客户端如果强制要求 nonce,反而会拿不到响应。建议做成可选的。
重要提醒:OCSP 不是银弹。如果你的系统对安全要求极高(比如金融、CA 自身),建议同时部署 CRL 和 OCSP,并且做好监控。我曾经见过一个案例,OCSP 响应者挂了三天,没人发现,期间所有吊销的证书都被当作有效证书在使用。