4、数字证书与PKI:X.509证书结构、CA体系、证书链验证、证书撤销机制

聊到 HTTPS,绕不开数字证书。

说白了,证书就是网站的网络身份证。你想想看,浏览器凭什么相信你访问的淘宝就是真淘宝?就是靠它手里的这张证书。

我早年排查过一个诡异的问题:某用户死活访问不了自家公司的网站,报证书错误。查了半天,发现他电脑时间比真实时间晚了两年。证书过期了。从那以后,我养成了先看系统时间的习惯。

4.1 X.509证书结构

目前用的最广的证书标准是 X.509 v3。v3 版本是 1996 年搞出来的,但到现在依然够用。因为它留了扩展字段的活口——你可以往里面塞自定义信息。

一份典型的 X.509 证书长什么样?我拆开给你看:

字段 说明 我的注释
版本号 (Version) v1 / v2 / v3,现在基本都是 v3 看到 v1 直接报警
序列号 (Serial Number) CA 分配的整型唯一标识 撤销时靠它定位
签名算法 (Signature Algorithm) e.g. sha256WithRSAEncryption 别用 SHA-1 了
颁发者 (Issuer) 签发这张证书的 CA 的 DN 根 CA 的 DN 是自签的
有效期 (Validity) notBefore ~ notAfter 主流证书现在最多 398 天
主体 (Subject) 证书持有者的 DN CN 字段常写域名
公钥 (Subject Public Key Info) 算法 + 公钥值 RSA 2048+ 或 ECC
扩展 (Extensions) [v3] 关键用途、SAN、KU 等 核心都在这里了
我个人习惯:拿到一张证书,先用 openssl x509 -text 看 extensions。SAN(Subject Alternative Name)字段尤其重要。我曾经见过一张证书的 CN 写对了,但 SAN 里没有这个域名,导致 Chrome 报错。Chrome 从 2017 年起就不看 CN 了,只看 SAN。

4.2 CA体系

CA(Certificate Authority),就是发证机关。但发证机关自己也得有证。这就引出了 CA 的层级结构。

通常分三层:

  • 根 CA (Root CA):自签证书。自己给自己签名。放在离线环境里,最安全。
  • 中间 CA (Intermediate CA):由根 CA 签发。平时干活用的。根 CA 一般不联网,中间 CA 负责日常发证。
  • 终端实体证书 (End-Entity):最终用户或服务器用的证书。由中间 CA 签发。

为什么搞这么复杂?

你想,如果根 CA 私钥泄露,所有信任都得重建。所以根 CA 的私钥通常锁在硬件安全模块(HSM)里,甚至锁在保险柜里。日常发证只动用中间 CA。即使中间 CA 挂了,撤销它就行,根 CA 还能继续用。

避坑指南:我在帮客户做内部 PKI 时,见过直接把根证书当服务器证书用的。浏览器会报信任错误,因为根证书不能用于服务器身份认证(basicConstraints 字段限制了)。根证书只做签发,不做业务。

4.3 证书链验证

浏览器拿到你的服务器证书后,不会直接相信它。它要沿着颁发者一路往上查,直到找到它信任的根证书。这条路径就是证书链。

验证过程大致是这样:

  1. 从服务器证书开始,提取 Issuer DN。
  2. 在服务器下发的中间证书里,找 Subject DN 匹配的那个。
  3. 用中间证书的公钥,验证服务器证书的签名。
  4. 重复,直到找到根证书。
  5. 检查根证书是否在浏览器的受信任根存储区里。

验证时还会检查几个关键扩展:

  • Key Usage (KU):比如 digitalSignature、keyEncipherment。
  • Extended Key Usage (EKU):比如 serverAuth、clientAuth。我记得有一次,一台 Web 服务器的证书 EKU 只配了 clientAuth,结果浏览器全挂了。
  • Basic Constraints:有没有 CA 标志,路径长度约束是多少。

这里面有个常见的坑:证书链不完整。很多运维只把服务器证书上配置了,没附带中间证书。手持 iOS 或 Android 设备的用户能访问,但 Windows 或桌面浏览器可能报错。为什么?因为操作系统内置的根存储不一样,有些客户端不会自动补全中间证书。你想想看,服务器应该把整条链(除根外)发给客户端。

我曾经踩过的坑:部署 Nginx 时,忘了把中间证书拼在 ssl_certificate 文件里。Chrome 能访问,但某老旧 Android 浏览器一直报错。查了三天,最后发现是证书链缺了中间 CA。请用 openssl s_client -connect 检查是否送了完整链。

下面这张图帮你看清证书链的信任逻辑:

证书链信任验证流程 证书链验证流程 Root CA (自签) Intermediate CA 服务器证书 签发 签发 信任锚点 内置于系统 验证过程 逐级验证签名 → 检查有效期 → 检查 KU / EKU / Basic Constraints → 检查撤销 → 信任

4.4 证书撤销机制

证书还没到期,私钥泄露了,怎么办?得把它废掉。

目前两种主流方案:

  • CRL (Certificate Revocation List):CA 定期公布的黑名单。里面有作废证书的序列号。我早年做项目时,CRL 列表经常有几百 KB 大,客户端下载慢得要命。
  • OCSP (Online Certificate Status Protocol):实时查询。客户端发请求问 CA "这张证书还活着吗?" 速度快,但 CA 的 OCSP 服务器压力大。

表里对比一下:

特性 CRL OCSP
实时性 差(定时更新) 好(实时查询)
带宽消耗 高(全量下载) 低(每次一条记录)
隐私 无泄露 CA 知道你在查谁
部署复杂度 简单 需要 OCSP Responder

现在还有一个变种叫 OCSP Stapling。啥意思?服务器自己定期去 CA 取 OCSP 响应,然后在下发证书时打包一起发给客户端。客户端不用再手动连 OCSP 服务器了。性能更好,也解决了隐私问题。我强烈建议开启。

注意:如果 OCSP 响应超时,浏览器的行为不太一样。Chrome 默认是软失败——查不到就不查了,允许访问。但某些银行内部系统要求硬失败——查不到就拒绝。你在做内部 PKI 时,得想清楚这个策略。
我个人建议企业环境用 OCSP Stapling + 软回退的 CRL,既快又兜底。

嗯,证书和 PKI 这块,核心就这些。理解 X.509 的结构,搞明白链式信任怎么传,再把握住撤销的时延风险,你在 HTTPS 部署上基本不会栽大跟头。