第3章 公钥基础设施(PKI)与数字证书:CA信任链、X.509证书格式、证书吊销

我先讲个真实案例。几年前我帮一家公司做内部系统改造,他们业务系统之间走的是自签名证书。结果某天突然崩了——一个服务升级后证书换掉了,其他服务没更新信任列表,全线拒绝连接。开发小哥急得满头大汗找我排查,一看登录日志,全是“certificate unknown”。

你想想看,如果当时有一套规范的PKI,根本不会出这种事。说白了,公钥基础设施就是解决“我怎么相信你拿的这把公钥真的属于你”这个问题的。你从网上随便下载一个公钥文件,敢直接用吗?不经过验证就信任,等于把门钥匙交给路人。

3.1 为什么需要PKI?

传统的对称加密,双方提前约定好密钥——这在小范围场景能行。但到了互联网上,服务器和浏览器谁都不认识谁,怎么把一个随机生成的密钥安全地传给对方?答案是对称加密+非对称加密混合,可关键一步来了:我怎么确定你给我的公钥确实是你本人的?

没有PKI的世界,你拿到一个公钥,既不知道持有者的身份,也不知道这把钥匙有没有被篡改。这时就需要一个大家公认的“中介”来担保。这个中介就是数字证书认证机构(CA)。CA给你签发一张数字证书,里面明明白白写着:“此公钥属于某某域名/组织,签名由我来担保”。你只要信任CA,就能信任CA担保的所有证书。

核心结论:PKI = 公钥 + 身份绑定 + CA担保 + 信任链验证。没有PKI,公钥分发就是裸奔。

3.2 X.509证书格式——一张身份证长什么样?

X.509是证书的“普通话”标准。不管你用RSA还是ECC,不管签发者是GlobalSign还是Let's Encrypt,证书的骨架都遵循这个格式。我经常用openssl命令来扒开证书看里面的字段:

openssl x509 -in certificate.pem -text -noout

你会看到一长串信息,但核心字段其实就那么几个。我整理了个表,方便你对照:

字段 英文名称 说明 我踩过的坑
版本号 Version 通常是v3(值为2) ——
序列号 Serial Number CA唯一标识一张证书的数字 某次CRL查吊销时,因为序列号大小写搞错查了半天
签名算法 Signature Algorithm 如sha256WithRSAEncryption ——
签发者 Issuer 签发这张证书的CA名称 有时候子CA名称写错了链路就断了
有效期 Validity notBefore 和 notAfter 证书过期是最常见的连接失败原因之一
主体 Subject 证书归属方,比如域名、组织 ——
公钥 Subject Public Key Info 算法+公钥值 我记得有次密钥长度太长被中间件限流
扩展 Extensions 关键用途(如CA:TRUE, 服务器身份等) 扩展写错了浏览器直接报“不安全”

实用技巧:用 openssl asn1parse 可以看到证书内部DER编码的结构树,适合深度调试。

3.3 证书信任链——一条通往信任的路径

现实世界你不可能只信任一个CA。地球上有几百家CA,每家又有好多级子CA。你的浏览器里预装了大约150-200个根CA证书。当你访问一个网站,服务器发来证书,浏览器做的事情其实就是——从服务器证书出发,一级一级往上找签发者,直到找到一个我信任的根CA为止。这个过程叫证书链验证。

我见过不少开发者只传一张终端证书,漏传中间CA证书,结果客户端拿到证书链不完整,验证失败。我自己在搭建内部CA的时候,也犯过这个错:根CA没配好中间CA的扩展,导致下级证书签完根本没法用。

证书信任链结构 根CA(自签) 二级CA(中间) 三级CA(中间) 服务器证书 客户端证书 验证路径:服务器证书 → 三级CA → 二级CA → 根CA(预信任)

经验之谈:生产环境一定要把中间CA证书跟服务器证书拼成一个.pem文件一起发给客户端。我习惯把中间CA放在上面,终端证书放下面,顺序错了可能有的库不认。

3.4 证书吊销——证书“作废”了怎么办?

证书还没到期,但私钥泄露了、域名换了、CA违规被撤了……这时候光靠有效期不够,必须有一种机制让CA通知所有人:这张证书已经失效,别再信了。这就是证书吊销。

目前主要有两种实现方式:

  • CRL(证书吊销列表):CA定期发布一个黑名单,里面列出所有已被吊销证书的序列号。客户端拿到后自己比对。
  • OCSP(在线证书状态协议):客户端实时问CA服务器“这张证书现在还有效吗?”,CA返回good/revoked/unknown。

OCSP响应更快,但有个痛点——每次握手都要多一次HTTP请求,拖慢速度。后来有了OCSP Stapling,让服务器自己把OCSP响应缓存好,握手时一起发给浏览器,省掉客户端的额外请求。不过,嗯,现实部署率还没那么高。

注意:我曾经遇到一个线上事故,CA的OCSP服务器因为流量过载挂了,所有浏览器都报“服务器证书状态未知”。后来不得不临时关闭OCSP验证才恢复正常。所以如果你做高可用系统,CA基础设施也要做冗余备份。

3.5 常见陷阱与最佳实践

讲几个我亲手处理过的坑,希望大家能绕过去:

  1. 证书链不完整:很多人在Nginx里只配了 server.crt 和 server.key,丢掉了中间CA。浏览器如果本地没有缓存中间CA,就会报“ERR_CERT_AUTHORITY_INVALID”。
  2. 证书扩展写错:比如给一个终端证书加了CA:TRUE(允许签子证书),或反过来给中间CA没加CA:TRUE。扩展跟用途不匹配,openssl verify过不了。
  3. 吊销检查超时:CRL文件越积越大,下载慢;OCSP服务器偶尔抽风。建议设置合理的超时时间,并给一个“soft-fail”选项(超时则视为有效,但不能完全信任)。
  4. 自己内部PKI的根CA私钥保护:我见过直接把根CA私钥放git repo里的……那不是给攻击者准备了万能钥匙吗?根CA私钥必须离线存储,用密码保护,最好用硬件安全模块(HSM)。

一句话总结:PKI是整个TLS信任的根基。证书格式、信任链、吊销机制这三块学扎实了,以后排查HTTPS问题你会相当顺手。