OCSP基本安全机制:签名、Nonce、时间戳
说实话,OCSP协议本身设计得很简洁。但简洁带来的问题就是——容易被人钻空子。尤其是重放攻击,你明明已经验证通过了一次,攻击者把旧的响应截下来,过段时间再扔给你,你如果没做防护,那就有可能再次放行。
怎么防?靠的就是三个核心机制:签名、Nonce 和 时间戳。这三者可以独立使用,也可以组合使用。我个人习惯是三者全上,因为每个机制防的攻击面不一样。
签名机制
签名是OCSP响应的第一层保护。它保证了两件事:响应确实来自授权的OCSP响应器,并且内容没有被篡改过。
你想想看,客户端收到一个响应,首先得确认这是不是真的来自那个响应器,对吧?签名就是干这个的。响应器用它的私钥对整个响应内容签名,客户端再用响应器证书里的公钥去验签。一旦验签失败,直接丢弃。
签名流程简述
- OCSP响应器构造响应内容。
- 使用私钥对响应内容的摘要进行签名。
- 将签名算法标识、签名值、签名证书链附加到响应中。
- 客户端收到后,提取签名证书,验证其可信性。
- 使用证书公钥验签,确认响应完整性。
// 伪代码示例:OCSP签名验证
function verifyOCSPSignature(response, trustedRoots):
// 1. 提取签名证书链
certChain = response.certificates
// 2. 验证证书链合法性
if !verifyCertChain(certChain, trustedRoots):
return false
// 3. 验签
signerCert = certChain[0]
expectedHash = hash(response.tbsData)
return verifySignature(signerCert.publicKey, response.signature, expectedHash)
Nonce机制
签名防住了篡改和伪造,但防不住重放——攻击者拿一个旧的合法响应来骗你,签名是有效的,怎么办?
这就轮到Nonce出场了。
Nonce说白了就是一个一次性随机数。客户端在请求里带上一个随机值,响应器把这个值原样塞回响应里。客户端收到响应后,检查Nonce是否跟自己发的那个一致。不一致?说明这个响应不是你刚才问的那个,有可能是重放的。
我曾经接手过一个旧系统,记得当时没有做Nonce检查,线上出了一个问题:某个证书的状态从“撤销”变成了“正常”,但客户端一直不更新。查了半天才发现,是代理层缓存了一个旧响应,客户端每次都拿那个旧响应做验证,结果永远认为证书是正常的。
Nonce的典型交互流程
| 步骤 | 客户端 | 响应器 |
|---|---|---|
| 1 | 生成随机Nonce | — |
| 2 | 将Nonce放入请求扩展 | — |
| 3 | 发送请求 | 接收请求,提取Nonce |
| 4 | — | 将Nonce原样放入响应扩展 |
| 5 | 验证响应中的Nonce是否匹配 | — |
// Nonce验证代码示例
byte[] sentNonce = generateRandomNonce();
request.setNonce(sentNonce);
// ... 发送请求,获取响应 ...
byte[] receivedNonce = response.getNonce();
if (!Arrays.equals(sentNonce, receivedNonce)) {
throw new SecurityException("Nonce不匹配,疑似重放攻击");
}
时间戳机制
如果响应器不支持Nonce怎么办?那就只能用时间戳来做粗略防护了。
OCSP响应里有两个时间字段:thisUpdate 和 nextUpdate。thisUpdate 表示响应的生成时间,nextUpdate 表示预期的下一个更新时间。客户端收到响应后,可以检查当前时间是否在 thisUpdate 和 nextUpdate 之间。如果当前时间比 nextUpdate 还晚,说明这个响应过期了,不能再用。
这个机制的问题在于:时间窗口可能很长。比如响应器设置 nextUpdate 为4小时后,那攻击者在这4小时内都能重放这个响应。
| 机制 | 防护类型 | 依赖条件 | 弱点 |
|---|---|---|---|
| 签名 | 数据完整性、源认证 | 可信的CA链、公钥基础设施 | 不防重放 |
| Nonce | 重放攻击 | 客户端生成随机数、响应器支持 | 部分响应器不支持 |
| 时间戳 | 过时响应重放 | 客户端时间同步 | 时间窗口内仍可重放 |
嗯,这里要注意——时间戳只能做辅助。我建议客户端同时获取NTP时间,不要完全依赖系统时间,否则你可能会被攻击者通过篡改系统时间来绕过检查。
三者结合使用效果最好。签名保真,Nonce防重,时间戳兜底。你想想看,即便Nonce匹配、签名有效,但如果响应已经过期了,那也不能用。
三种机制的关系总结
看到这张图了吗?签名、Nonce、时间戳是三道防线。签名最先做,因为它保的是基础和信任链;然后是Nonce检查,确认响应是新鲜的;最后用时间戳兜底,防止漏网之鱼。任何一个环节出问题,我都建议直接丢弃响应,别给攻击者留机会。
好了,这一章的核心就是这三把锁。下一轮我们聊聊怎么在客户端把这些检查串起来,形成一个完整的验证流程。