4、证书链基础:X.509证书结构、CA根证书、中间证书、叶子证书

说到固件保护,绕不开证书链。我见过不少团队,加密算法选得挺强,结果在证书链上翻了车。说白了,证书链就是一套信任传递机制。你信任谁,谁又信任谁,一层层往上追溯,直到一个你无条件信任的根。

这一章,咱们把X.509证书的结构拆开看看。然后聊聊根证书、中间证书、叶子证书各自扮演什么角色。嗯,这些概念搞清楚了,后面做固件签名验证才不会踩坑。

4.1 X.509证书长什么样?

X.509是公钥证书的国际标准。你打开一个证书文件,里面不是只有公钥。它包含了一大堆字段。我习惯把它分成三块:

  • 基本信息:序列号、签名算法、颁发者、有效期、使用者
  • 公钥信息:算法标识、公钥值
  • 扩展信息:密钥用途、基本约束、主题备用名称等

举个例子,一个典型的X.509 v3证书结构如下:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 0x12345678
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=MyRootCA, O=MyCompany, C=CN
        Validity:
            Not Before: Jan  1 00:00:00 2024 GMT
            Not After : Dec 31 23:59:59 2026 GMT
        Subject: CN=MyDeviceCert, O=MyCompany, C=CN
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Key Usage: critical
                Digital Signature
    Signature Algorithm: sha256WithRSAEncryption
        签名值(一段二进制数据)

这里有个关键点——签名算法和签名值。证书本身是自包含的:颁发者用自己的私钥给证书内容签名,验证者用颁发者的公钥验签。我在项目中遇到过有人把签名算法和公钥算法搞混,结果验签一直失败。记住,签名算法是给证书内容签名的,公钥算法是后续做加密或签名验证用的。

核心字段速查表

字段 含义 我的提醒
Issuer 颁发者DN 必须与上级证书的Subject一致
Subject 使用者DN 叶子证书通常是设备ID或域名
Validity 有效期 固件证书过期会导致OTA失败,我吃过亏
Basic Constraints 是否CA证书 CA:TRUE才能签发下级证书
Key Usage 密钥用途 签名证书不能用来加密,反之亦然

4.2 根证书:信任的起点

根证书是证书链的锚点。它自己给自己签名,这叫自签名证书。你想想看,如果连根证书都不信任,整个链条就断了。

根证书有几个特点:

  • 自签名:Issuer和Subject是同一个DN
  • CA标志为TRUE:Basic Constraints里CA:TRUE
  • 长期有效:通常10年、20年甚至更长
  • 离线存储:根私钥必须离线保管,物理隔离

我曾经见过一个团队,把根私钥放在开发服务器上,结果被拖库。嗯,那场面……整个产品线的证书都得重新签发。所以我的建议是:根私钥生成后,立刻导入硬件安全模块(HSM),然后从服务器上彻底删除。

⚠️ 根证书保护原则

根私钥一旦泄露,所有下级证书都不再可信。必须离线、加密、多副本、物理隔离存储。我在项目中强制要求:根私钥操作需要两人同时在场,且操作记录全程审计。

4.3 中间证书:承上启下

直接拿根证书给设备签发叶子证书,风险太大。万一叶子证书私钥泄露,根证书就得吊销,所有设备都得更新。所以业界做法是:根证书只签发中间证书,中间证书再签发叶子证书。

中间证书的特点:

  • 由根证书签发:Issuer是根证书的Subject
  • CA标志为TRUE:可以继续签发下级证书
  • 有效期较短:通常2-5年
  • 可以有多级:根→中间1→中间2→叶子

这样做的好处很明显。如果某个中间证书出问题,只需要吊销它,重新签发一个。根证书纹丝不动。我习惯在固件更新场景里用两级中间证书:一级用于产品线,一级用于批次。这样隔离更细。

4.4 叶子证书:最终实体

叶子证书就是最终使用的证书。在固件保护里,它通常绑定到具体的设备或固件版本。叶子证书的CA标志为FALSE,不能签发任何下级证书。

叶子证书的用途:

  • 固件签名:用叶子证书的私钥对固件哈希签名
  • 设备身份:证书Subject包含设备唯一标识
  • 短期有效:建议1年以内,方便轮换

这里有个坑。叶子证书的Key Usage必须正确设置。如果用来签固件,就要设置Digital Signature。如果用来做TLS通信,还要加上Key Encipherment。我遇到过有人把叶子证书的Key Usage设成nonRepudiation,结果验签时死活通不过。后来查标准才发现,nonRepudiation和digitalSignature是两码事。

💡 我的经验

固件签名场景下,叶子证书的Key Usage只保留digitalSignature就够了。别贪多,多了反而容易出兼容性问题。我曾经为了省事,把一堆用途都勾上,结果某个老版本验签库直接报错。

4.5 证书链验证流程

验证证书链,说白了就是回答一个问题:这个叶子证书是不是由我信任的根证书签发的?

验证步骤:

  1. 从叶子证书开始,取出Issuer字段
  2. 找到Issuer对应的中间证书(或根证书)
  3. 用上级证书的公钥验证下级证书的签名
  4. 检查下级证书的有效期、吊销状态
  5. 重复步骤1-4,直到遇到自签名的根证书
  6. 检查根证书是否在信任列表中

如果中间任何一步失败,整个链就断了。我在调试固件OTA时,经常遇到证书链不完整的问题——设备只存了叶子证书,没存中间证书。服务器下发固件时也没带中间证书,结果设备验签失败。嗯,这个问题排查起来挺费劲的。

下面这张图展示了完整的证书链结构:

证书链结构图 根证书 (Root CA) 自签名 · CA:TRUE 有效期:20年 签发 中间证书 (Intermediate CA) 由根签发 · CA:TRUE 有效期:5年 签发 叶子证书 (Leaf/Device Cert) 由中间签发 · CA:FALSE 有效期:1年 · KeyUsage: DigitalSignature 验证路径 设备固件 → 叶子证书 → 中间证书 → 根证书(信任锚) 每一级都用上级公钥验签,全部通过才算可信

4.6 固件保护中的证书链实践

在固件签名场景里,证书链怎么用?我直接说我的做法:

  • 根证书:预埋在设备BootROM或安全存储区,不可更新
  • 中间证书:随固件更新包下发,设备验证后缓存
  • 叶子证书:用于签名固件哈希,私钥保存在签名服务器HSM中

设备启动时,先验证固件签名。验证流程:

  1. 从固件头中提取叶子证书和签名值
  2. 用预置的根证书验证中间证书
  3. 用中间证书验证叶子证书
  4. 用叶子证书的公钥验证固件哈希签名
  5. 全部通过,固件可信,启动

这里有个细节。设备存储空间有限,不可能存整个证书链。我一般只预埋根证书,中间证书和叶子证书都放在固件包里。设备验证时动态构建证书链。嗯,这样既安全又省空间。

证书链各环节对比

角色 谁签发 CA标志 存储位置 私钥保护
根证书 自签名 TRUE 设备ROM / HSM 离线物理隔离
中间证书 根证书 TRUE 固件包 / 服务器 HSM或加密存储
叶子证书 中间证书 FALSE 固件包头部 签名服务器HSM

最后说一句。证书链不是越复杂越好。我见过有人搞了五级中间证书,结果验证时间长了十倍,设备启动都超时了。一般来说,根+一级中间+叶子就够用了。除非你有特别的分级管理需求,否则别给自己找麻烦。