第3章:公开密钥基础设施(PKI)——数字证书、CA、证书链
聊完对称与非对称加密,你可能有个疑问:我拿到了Bob的公钥,怎么确认这真是Bob的?万一被篡改了呢?
这就是PKI要解决的问题。说白了,PKI是一套信任体系。它用数字证书把公钥和身份绑在一起。我当年刚接触TLS时,总觉得这层是“多此一举”,直到我在项目中排查了一次中间人攻击……嗯,从那以后,我对证书环节再也不敢马虎了。
3.1 数字证书:公钥的“身份证”
数字证书就是一个电子文件。它把公钥、持有者身份、有效期等信息打包在一起,然后由一个大家都信任的机构签名。
证书的核心字段,我挑几个常用的给你看:
- 版本号:目前主流是V3。
- 序列号:CA分配的唯一编号。
- 签名算法:CA用的是SHA256还是SHA384。
- 颁发者:签发这张证书的CA名称。
- 有效期:不是越长越好。我见过被泄露的私钥因为有效期太长,挂了半年才被吊销。
- 主体:证书归属,比如域名或组织名。
- 公钥:RSA、ECC等,含算法参数。
- 扩展:用途(服务器认证/代码签名)、CRL分发点等。这个地方容易踩坑,我后面会讲。
openssl x509 -in cert.pem -text -noout。你一眼就能看到上面的所有字段。
3.2 CA:谁来发证?
CA就是那个“大家都信任的机构”。它负责验证申请者身份,然后签发证书。
CA分三个类型:
- 公共CA:DigiCert、Let's Encrypt、GlobalSign等。浏览器和OS默认信任它们。
- 私有CA:企业自己搭建的内部CA。我所在的公司内部系统用的就是自建CA,方便控制,成本也低。
- 自签名证书:自己给自己签。开发测试时常用,但生产环境千万别这么干——浏览器会报警。
你可能会问:凭什么信任公共CA?因为它们的根证书预先安装在你的系统里了。你安装浏览器或OS时,那个信任列表就已经固定了。
重点: 公共CA的根证书是信任的起点。如果有人在你的设备里塞了一个恶意的根证书,那你访问任何网站都可能被劫持。我曾在安全审计中发现有人私自安装了未知CA的根证书——这就是一个高危漏洞。建议你定期检查系统信任库,看看有没有不认识的根证书。
3.3 证书链:信任的传递
CA不会直接用根证书签每一个网站的证书。因为根证书的私钥一旦泄露,整个信任体系就崩塌了。所以CA的做法是:
- 根CA:自签名,离线存储,极其安全。
- 二级CA:由根CA签发,负责实际签发终端证书。
- 终端证书:给最终用户(如你的服务器)用的。
这三层连起来,就是证书链。浏览器验证时,从终端证书开始,逐级向上验证签名,直到找到系统信任的那个根证书。
我曾经在一次部署中遇到了证书链不完整的问题——我把终端证书配置了,但没把二级CA证书带上。结果Android手机访问时报错,而电脑上的Chrome没事。为什么?因为Chrome自己会补全缺少的中间证书,但Android不会。嗯,从那以后我每次部署都会检查证书链是不是完整的,用openssl验证一下:
openssl verify -CAfile root.crt -untrusted intermediate.crt server.crt
这条命令检查的是:用root.crt和intermediate.crt来验证server.crt是否合法。如果报错,就是链有问题。
3.4 信任模型
PKI的信任模型主要有三种,我列个表方便你对比:
| 模型 | 运作方式 | 优缺点 |
|---|---|---|
| 层次模型 | 一个根CA,下面挂二级CA、三级CA,形成树状 | 结构清晰,但根CA是单点故障 |
| 网状模型 | 多个CA互相交叉签名 | 灵活性高,但路径复杂,性能下降 |
| 桥接模型 | 引入一个桥CA,连接不同PKI体系 | 适合异构系统,但依赖桥CA的可用性 |
实际互联网上,浏览器用的就是层次模型的变种。每个浏览器厂商维护自己的根证书列表,然后根证书再签发二级CA。你想想看,如果这个体系里有一个根证书被篡改,那全球一半的网站都会出问题。所以根证书的保管极其严格,我听说有的CA把根证书私钥存在离线机房里,进门要指纹+虹膜+物理钥匙三重认证。
注意: 证书链的长度不是越长越好。每多一级,验证时的计算开销就增加一次。而且有些浏览器对证书链深度有限制(通常是5~6级)。我曾经见过一个客户,他们的证书链居然有7级,结果Safari直接报错。
3.5 证书吊销
证书不是永恒有效的。私钥泄露、域名变更、组织改名……都需要吊销证书。吊销机制有两种:
- CRL(证书吊销列表):CA定期发布一个列表,列出已被吊销的证书序列号。问题是它不是实时的,而且列表越长,下载越慢。
- OCSP(在线证书状态协议):实时查询单个证书是否有效,响应速度快很多。但OCSP服务器如果挂了,你就查不了。
我建议你优先用OCSP,尤其是对性能敏感的场景。但OCSP也有个问题:它暴露了用户正在访问哪个网站。为了解决隐私问题,后来又出现了OCSP Stapling——由服务器主动在TLS握手时附带OCSP响应,浏览器就不用再单独去查询了。这个优化很有用,我做过测试,启用OCSP Stapling后,握手时延平均减少了30ms。
经验分享: 在Nginx里启用OCSP Stapling很简单:
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/root.crt;
记得把根证书和中间证书都放在trusted_certificate里。我曾经漏掉中间证书,结果stapling一直不生效。
3.6 知识体系总览
我画了一张图,把本章的核心脉络串起来。你扫一眼就能明白PKI各组件之间的关系:
3.7 证书格式与编码
证书的编码格式主要有两种:
- PEM:纯文本,以“-----BEGIN CERTIFICATE-----”开头,Base64编码。最常见的格式,配置方便。
- DER:二进制格式。Java环境或Windows系统常见。
转换也很简单:
# DER → PEM
openssl x509 -inform der -in cert.der -out form pem -out cert.pem
# PEM → DER
openssl x509 -outform der -in cert.pem -out cert.der
另外还有一个PFX/P12格式,它是把证书和私钥打包在一起的容器,通常用于IIS或Windows服务。我个人建议不要在TLS配置里直接用PFX,除非你有特殊需求——因为一旦分发出去,私钥就离开了安全区。
总结一下: 证书就是公钥的身份证明,CA是发证机关,证书链是信任传递的路径。这三者加在一起,才构成了互联网上可信的通信基础。没有PKI,你每次访问的“https://”都可能是一个陷阱。