普通curl请求成功,一加--cert-status就报91,错误写着No OCSP response received。这不等于证书过期,也不能直接认定证书已被吊销。本文用回环TLS实验拆开常规证书验证与OCSP装订检查,比较缺失、good和revoked响应,最后给出不会把诊断结果当验收的命令。
一、先看91检查的到底是什么
curl的--cert-status通过TLS证书状态请求扩展检查服务端装订的OCSP响应。官方手册明确:没有响应、响应无效或提示证书已吊销,都会导致验证失败;libcurl将相关失败列为CURLE_SSL_INVALIDCERTSTATUS,即91。
这里"没有响应"指握手没有拿到需要的装订材料,不是说网站没有HTTP正文。证书链受信、名称匹配和状态检查是不同条件。把CA文件换三遍,也不能让没有发送的OCSP响应突然出现。

二、把版本和普通请求留作对照
先保存curl版本及后端。当前官方手册列出的该选项支持后端为OpenSSL与GnuTLS,不能把本文输出推广到所有客户端。以下命令针对已准备好的本地测试服务:证书SAN包含localhost,ca.pem是经确认的测试CA,8443须替换为实际监听端口。
bash
curl --version
openssl version
bash
curl -q --noproxy '*' --cacert ca.pem \
--connect-timeout 3 --max-time 8 \
--fail --silent --show-error \
--output baseline.txt --write-out '%{http_code}\n' \
https://localhost:8443/
-q放在首个选项以跳过默认curlrc,避免隐藏配置改变对照;--noproxy仅用于这个回环实验,不是建议绕过生产网络策略。普通请求成功只建立一条基线,不能据此宣布OCSP策略已满足。正式排查要换成获授权的目标和可信材料,不安装测试CA到系统信任库。
三、用s_client观察装订,别把观察当裁判
下面固定连接地址、发送SNI,并显式校验名称和证书链。输出落盘后,在status.txt查看OCSP response及Cert Status字段,错误日志也要一起保留。命令在Bash中执行,诊断目录应只向必要用户开放。
bash
openssl s_client -connect 127.0.0.1:8443 \
-servername localhost -verify_hostname localhost \
-verify_return_error -CAfile ca.pem -status \
</dev/null > status.txt 2> status.err
rc=$?
[ "$rc" -eq 0 ] || exit "$rc"
关键边界是:-status用于请求并显示装订响应,不能把命令退出0直接解释为OCSP通过。本轮即使返回revoked,s_client也打印了对应状态并退出0;没有装订时同样可在常规证书验证通过后退出0。-verify_return_error不能自动补成curl --cert-status的语义。
别只搜索Verify return code: 0,那行与装订不是同一个判定对象。
四、隔离实验的正反例
本轮使用curl 7.61.1的OpenSSL后端、OpenSSL 1.1.1k FIPS命令行,在127.0.0.1随机端口运行TLS 1.2 s_server。临时CA签发localhost证书,并制作good与revoked的DER响应,通过-status_file装订;结束销毁证书、私钥与监听,未修改系统时间、信任库或生产配置。
| 测试条件 | curl退出码 | 观察 |
|---|---|---|
| 不装订,普通请求 | 0 | HTTP 200 |
| 不装订,加--cert-status | 91 | No OCSP response received |
| 有效good装订,加状态检查 | 0 | HTTP 200 |
| revoked装订,加状态检查 | 91 | 报告吊销相关错误 |
| good装订,换成无关CA | 60 | 常规链信任仍然失败 |
失败请求的write-out为000,意思是未取得HTTP状态,不是服务器返回"000"。本轮只验证上述分支,没有把响应过期、代理、浏览器或所有TLS版本说成已实测。过期等无效响应的失败行为来自官方说明。
夹具还有个细节:本机旧curl最初在根直签且服务端仅发叶证书时报Error computing OCSP ID。为保证对照能定位状态检查,测试服务补发签发者后good通过。这个实验安排不等于生产应发送根证书;生产要按真实证书层级正确部署中间链。
五、按响应缺失、无效、吊销分别处理
没有装订,先确认请求落在哪个TLS终止点:CDN、负载均衡还是源站。检查该终止点是否支持并启用装订、证书签发体系是否提供适用的状态服务,以及它实际发送的证书和链。只在源站打开一个开关,不能证明边缘节点也已更新。
已有响应但验证失败,要核对响应对应的证书、签发者、签名与时间窗口;续期换证后仍缓存旧响应也是待排查方向,不能仅因文件"存在"就当正确。如果明确为revoked,应按证书事件处理流程更换材料并调查原因,而不是延长缓存或忽略错误。
若证书体系本来不提供适用装订,先确认监控要求是否成立,再由负责安全策略的人决定检测方式。删掉--cert-status只是撤销这项检查,不是修复装订;不要用-k关闭常规身份验证,也不要把改成绿灯当成问题解决。
六、用要求不变的请求验收
对于明确要求装订的目标,修复后仍须保留--cert-status。下面同时要求常规证书验证、状态检查、curl成功和测试接口HTTP 200;诊断文件不可写也应使脚本失败,而非悄悄丢证据。
bash
umask 077
rc=0
curl -q --noproxy '*' --cacert ca.pem --cert-status \
--connect-timeout 3 --max-time 8 \
--fail --silent --show-error \
--output body.txt --write-out '%{http_code}' \
https://localhost:8443/ > probe.status 2> probe.err || rc=$?
[ "$rc" -eq 0 ] || exit "$rc"
[ "$(<probe.status)" = 200 ] || exit 1
200是本文测试接口契约,不是所有业务的唯一成功状态。--fail处理HTTP错误;实际应用还应核对必要的业务响应。本轮重放四段最终代码,缺失与revoked装订都不能通过最后的包装器,错误日志目录不存在也会阻止通过。
七、最后按清单交付
验收清单:记录版本与TLS后端;确认授权目标和TLS终止点;普通请求与状态检查使用同一身份和可信CA;区分无装订、无效与revoked;保留完整退出码和日志;按不变的安全要求重复探测。多节点场景逐个覆盖已知目标,不能拿一次成功代表所有入口。
本文验证的是隔离TLS 1.2测试,不是生产证书吊销状态报告。离线DER文件也不是可以长期复制使用的生产响应。真正要交付的是"检查了什么、为什么通过",而不只是一行绿色成功。