3、密码学基础(二):公钥基础设施(PKI)与证书链,X.509证书结构解析

上一讲我们聊了对称与非对称加密、哈希和签名这些基础工具。但有个问题一直悬着:你拿到了 Bob 的公钥,你怎么确定这公钥真的是 Bob 的?

这就是 PKI 要解决的核心问题——信任的起点。说白了,PKI 就是一套让公钥变得可信的管理体系。我早年做物联网固件升级时,就踩过没有 PKI 的坑:设备直接硬编码了一个公钥,结果密钥泄露后,所有设备都得返厂升级。嗯,从那以后,我再也不敢轻视证书管理了。

3.1 什么是 PKI?

公钥基础设施(Public Key Infrastructure,PKI)不是某个单一技术,而是一整套策略、硬件、软件和流程的组合。它的目标就三个:

  • 绑定身份与公钥:通过数字证书,把“张三”和“张三的公钥”锁在一起。
  • 提供信任路径:从你信任的根,一路验证到目标证书。
  • 支持生命周期管理:证书颁发、更新、吊销,全流程可控。

PKI 的核心角色有这几个:

角色 全称 职责
CA 证书颁发机构 签发和吊销证书,PKI 的信任核心
RA 注册机构 验证申请者身份,把审核结果交给 CA
VA 验证机构 提供证书状态查询(如 OCSP 响应)
EE 终端实体 持有证书和私钥的最终用户或设备

我个人习惯把 PKI 比作身份证系统:CA 就是公安局,RA 是派出所,VA 是联网查询系统,EE 就是你手里的身份证。你想想看,没有公安局,谁敢随便相信一张身份证?

3.2 X.509 证书结构

X.509 是 PKI 中最通用的证书格式标准。我见过的嵌入式设备,从车规级 MCU 到低功耗蓝牙芯片,几乎都在用 X.509 v3 版本。它的结构其实很清晰,咱们拆开来看。

3.2.1 证书的基本字段

一个标准的 X.509 v3 证书包含以下核心字段:

  • 版本号(Version):目前主流是 v3(值为 2)。
  • 序列号(Serial Number):CA 颁发的唯一整数,用于标识证书。
  • 签名算法(Signature Algorithm):CA 签名时用的算法,比如 sha256WithRSAEncryption。
  • 颁发者(Issuer):签发该证书的 CA 的 Distinguished Name(DN)。
  • 有效期(Validity):notBefore 和 notAfter,定义了证书的生效和过期时间。
  • 主体(Subject):证书持有者的 DN。
  • 主体公钥信息(Subject Public Key Info):持有者的公钥算法和公钥值。
  • 扩展(Extensions):v3 引入的灵活区域,用于附加约束和用途。

这里有个细节我提醒一下:序列号在 CA 内部必须唯一。我曾经遇到过一个项目,CA 的序列号生成器出了 bug,重复了,结果证书链验证时出现了歧义。排查了两天才找到根因。

3.2.2 关键扩展字段

v3 扩展是 X.509 的精华。没有扩展,证书就是个简单的公钥容器。有了扩展,我们才能做细粒度的访问控制。

扩展名称 OID 作用
Basic Constraints 2.5.29.19 标记是否为 CA 证书,以及路径长度限制
Key Usage 2.5.29.15 限制密钥用途(数字签名、密钥加密等)
Extended Key Usage 2.5.29.37 更细粒度的用途(服务器认证、代码签名等)
Subject Alternative Name 2.5.29.17 主体别名,常用于 HTTPS 的多域名支持
CRL Distribution Points 2.5.29.31 指定证书吊销列表的获取地址
Authority Information Access 1.3.6.1.5.5.7.1.1 提供 CA 证书和 OCSP 响应器的地址

做固件签名时,我最关注的是 Key Usage 和 Extended Key Usage。比如,代码签名证书必须包含 digitalSignature 用途,并且 EKU 里要有 codeSigning。如果这两个没设对,操作系统或 Bootloader 会直接拒绝加载固件。

我的经验:在嵌入式 PKI 设计中,建议把 Basic Constraints 的 pathLenConstraint 设小一点。比如,根 CA → 二级 CA → 终端证书,pathLenConstraint 设为 0 或 1。这样即使二级 CA 被攻破,攻击者也无法签发更多中间 CA,缩小了爆炸半径。

3.3 证书链与信任模型

证书链(Certificate Chain)是 PKI 里最核心的概念之一。你想想看,如果每个设备都要内置所有 CA 的根证书,那维护成本就太高了。证书链的思路是:你只需要信任一个根,然后通过链式验证,信任所有由该根签发的证书。

3.3.1 证书链的结构

一个典型的证书链有三层:

  1. 根 CA 证书(Root CA):自签名,信任锚点。通常离线存储,极其安全。
  2. 中间 CA 证书(Intermediate CA):由根 CA 签发,用于签发终端证书。可以有多级。
  3. 终端实体证书(Leaf/EE):由中间 CA 签发,用于实际通信或签名。

验证过程是从叶子往根走:

  • 用中间 CA 的公钥验证叶子证书的签名。
  • 用根 CA 的公钥验证中间 CA 证书的签名。
  • 根 CA 是自签名的,你必须在本地信任它。

这里有个常见的误解:验证证书链时,不需要验证根证书的签名。因为根证书是信任锚点,你信任它,不是因为它签名了自己,而是因为你把它放进了信任存储区。

3.3.2 证书链验证的完整流程

我习惯把验证流程拆成四步:

  1. 有效期检查:每个证书的当前时间必须在 notBefore 和 notAfter 之间。
  2. 签名验证:从叶子开始,用上一级证书的公钥验证当前证书的签名。
  3. 名称链检查:当前证书的 Issuer 必须与上一级证书的 Subject 一致。
  4. 扩展约束检查:Basic Constraints、Key Usage、Path Length 等必须合规。
注意:很多嵌入式系统为了省资源,只做了签名验证,跳过了有效期和扩展检查。这是非常危险的。我曾经见过一个案例:攻击者拿了一个已过期的中间 CA 证书,签发了恶意固件,而设备因为没检查有效期,直接放行了。所以,四步检查,一步都不能少。

3.4 证书生命周期管理

证书不是永久的。从颁发到销毁,每个阶段都有对应的管理动作。

阶段 动作 说明
初始化 密钥生成、CSR 提交 私钥必须在安全环境中生成,CSR 包含公钥和身份信息
颁发 CA 签发证书 CA 验证 CSR 后,签发 X.509 证书并返回
使用 身份认证、签名、加密 证书在有效期内正常使用
更新 证书续期 在过期前重新签发,密钥可以不变或轮换
吊销 CRL / OCSP 私钥泄露或人员离职时,立即吊销证书
销毁 私钥安全删除 证书过期或吊销后,私钥必须彻底清除

吊销这块我多说两句。在嵌入式场景里,设备可能常年离线,没法实时查询 CRL 或 OCSP。怎么办?我常用的方案是短有效期证书:把证书有效期设为几天甚至几小时,过期自动失效,不需要吊销。这在物联网固件签名中特别实用。

3.5 证书链的视觉化

下面这张图展示了从根 CA 到终端设备的完整证书链结构,以及验证时的数据流向。

证书链结构与验证流程 根 CA 证书 自签名 · 信任锚点 签发 中间 CA 证书 由根 CA 签发 签发 终端实体证书 由中间 CA 签发 验证方向 验证逻辑: 从叶子证书开始,逐级用上一级证书的公钥验证当前证书的签名,直到信任的根 CA。

3.6 嵌入式 PKI 的常见坑

做嵌入式 PKI 和做 Web PKI 差别很大。我总结几个实际项目中踩过的坑:

  • 证书太大:X.509 证书动辄 1-2KB,对于只有几十 KB Flash 的 MCU 来说,存几个证书就满了。我建议用证书压缩或只存指纹,验证时再获取完整证书。
  • 时间同步难:很多设备没有 RTC 或无法联网校时,有效期检查就成了摆设。我的做法是用固件版本号代替时间戳,或者用单调计数器做相对时间验证。
  • 私钥存储:私钥必须放在安全元件(SE)或 TEE 里。如果放在普通 Flash 里,攻击者用 JTAG 就能读出来。嗯,这个坑我早年也踩过。
  • 证书链深度:链太长会导致验证时间变长,而且占用更多 RAM。我一般控制在 3 层以内(根 + 中间 + 叶子)。
核心要点:PKI 的本质是信任的传递。你不需要信任每一个实体,只需要信任一个根,然后通过证书链把信任传递下去。X.509 证书就是这种信任的载体,而扩展字段让我们能精细控制每个证书的用途和约束。