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 证书链验证流程
验证证书链,说白了就是回答一个问题:这个叶子证书是不是由我信任的根证书签发的?
验证步骤:
- 从叶子证书开始,取出Issuer字段
- 找到Issuer对应的中间证书(或根证书)
- 用上级证书的公钥验证下级证书的签名
- 检查下级证书的有效期、吊销状态
- 重复步骤1-4,直到遇到自签名的根证书
- 检查根证书是否在信任列表中
如果中间任何一步失败,整个链就断了。我在调试固件OTA时,经常遇到证书链不完整的问题——设备只存了叶子证书,没存中间证书。服务器下发固件时也没带中间证书,结果设备验签失败。嗯,这个问题排查起来挺费劲的。
下面这张图展示了完整的证书链结构:
4.6 固件保护中的证书链实践
在固件签名场景里,证书链怎么用?我直接说我的做法:
- 根证书:预埋在设备BootROM或安全存储区,不可更新
- 中间证书:随固件更新包下发,设备验证后缓存
- 叶子证书:用于签名固件哈希,私钥保存在签名服务器HSM中
设备启动时,先验证固件签名。验证流程:
- 从固件头中提取叶子证书和签名值
- 用预置的根证书验证中间证书
- 用中间证书验证叶子证书
- 用叶子证书的公钥验证固件哈希签名
- 全部通过,固件可信,启动
这里有个细节。设备存储空间有限,不可能存整个证书链。我一般只预埋根证书,中间证书和叶子证书都放在固件包里。设备验证时动态构建证书链。嗯,这样既安全又省空间。
证书链各环节对比
| 角色 | 谁签发 | CA标志 | 存储位置 | 私钥保护 |
|---|---|---|---|---|
| 根证书 | 自签名 | TRUE | 设备ROM / HSM | 离线物理隔离 |
| 中间证书 | 根证书 | TRUE | 固件包 / 服务器 | HSM或加密存储 |
| 叶子证书 | 中间证书 | FALSE | 固件包头部 | 签名服务器HSM |
最后说一句。证书链不是越复杂越好。我见过有人搞了五级中间证书,结果验证时间长了十倍,设备启动都超时了。一般来说,根+一级中间+叶子就够用了。除非你有特别的分级管理需求,否则别给自己找麻烦。