3、密码学基础(下):公钥基础设施(PKI)、证书链、信任根
上一讲我们聊了非对称加密和哈希,说白了就是解决了「钥匙怎么分」和「内容改没改」的问题。但光有这两样还不够——你拿到一把公钥,怎么知道它真的是设备厂商的,而不是中间人塞给你的?
这就是 PKI 要干的事。我当年第一次接触固件签名时,觉得 RSA 验签就完事了,结果被问了一句「你的根公钥存在哪?」直接问住了。嗯,今天我们就来把这个坑填上。
3.1 什么是 PKI?
公钥基础设施(PKI),不是某个算法,而是一整套管理密钥和证书的体系。它解决三个核心问题:
- 身份认证:这把公钥到底是谁的?
- 完整性:公钥本身有没有被篡改?
- 不可否认性:签名者事后不能抵赖说「我没签过」
你想想看,如果固件里硬编码一把公钥,攻击者只要逆向固件替换掉它,就能用自己的私钥签恶意固件。PKI 通过证书链和信任根,让这种替换变得极其困难。
核心概念:PKI 不是产品,是框架。它定义了 CA(证书颁发机构)、RA(注册机构)、证书库、CRL(证书吊销列表)等角色和流程。
3.2 证书链——信任的传递
证书链,说白了就是「你爸爸的爸爸是谁」。一个典型的固件签名证书链有三层:
- 根证书(Root CA):自签名,信任的起点。一般离线保存,极其安全。
- 中间证书(Intermediate CA):由根证书签发,用于日常签发终端证书。
- 终端证书(End-entity / Leaf):真正用来签固件的证书。
为什么要搞这么复杂?直接根证书签固件不行吗?
我在一个 IoT 项目里见过这种设计,结果根私钥被开发人员误传到 Git 仓库里了……幸好发现得早。如果根私钥泄露,整个产品线的信任体系都得重建。用中间证书隔离后,就算终端证书泄露,吊销它就行,根证书纹丝不动。
| 层级 | 签发者 | 安全级别 | 典型用途 |
|---|---|---|---|
| 根证书 | 自签名 | 最高(离线存储) | 信任锚点 |
| 中间证书 | 根证书 | 高(受控环境) | 签发终端证书 |
| 终端证书 | 中间证书 | 中等 | 固件签名 |
3.3 信任根——信任的起点
信任根(Root of Trust, RoT)是整个 PKI 体系的物理锚点。在嵌入式世界里,它通常是一个不可篡改的硬件模块,比如:
- HSM(硬件安全模块):专用加密芯片,密钥永远不离开硬件
- TPM(可信平台模块):PC/服务器常用,提供受保护的密钥存储
- eFuse / OTP(一次性可编程存储器):芯片出厂时烧录根公钥哈希,之后无法修改
- 安全元件(SE):手机、支付终端里常见,独立 CPU + 存储
避坑指南:我曾经见过一个团队把根公钥放在外部 Flash 里,用 CRC 校验防篡改。结果攻击者直接替换了 Flash 内容和 CRC 值……记住:信任根必须是硬件不可逆的,软件层面的保护都是纸老虎。
3.4 固件签名验证流程
一个完整的固件签名验证流程,我习惯把它拆成三步:
- 构建证书链:从固件携带的终端证书开始,逐级向上验证,直到信任根
- 验证证书有效性:检查有效期、吊销状态、密钥用法
- 验证固件签名:用终端证书的公钥验签
代码层面,一个典型的验证逻辑长这样:
// 伪代码:固件签名验证
int verify_firmware(uint8_t *fw, uint32_t fw_len,
uint8_t *leaf_cert, uint32_t leaf_len,
uint8_t *signature, uint32_t sig_len) {
// 1. 从硬件读取信任根公钥
uint8_t root_pubkey[64];
hsm_read_root_pubkey(root_pubkey);
// 2. 验证证书链(从叶子到根)
cert_chain_t chain;
if (!build_cert_chain(&chain, leaf_cert, root_pubkey)) {
return ERR_CHAIN_INVALID; // 证书链断裂
}
// 3. 检查证书吊销状态(CRL/OCSP)
if (is_cert_revoked(chain.leaf)) {
return ERR_CERT_REVOKED;
}
// 4. 用叶子证书的公钥验签
if (!ecdsa_verify(fw, fw_len, signature, chain.leaf->pubkey)) {
return ERR_SIG_INVALID;
}
return OK;
}
个人经验:实际项目中,证书链验证的「递归深度」一定要做限制。我遇到过攻击者构造一个超长证书链导致栈溢出的案例。建议限制最多 4 层(根 + 2 个中间 + 叶子)。
3.5 证书吊销——被遗忘的角落
很多人做完签名验证就觉得万事大吉了,但证书是会过期的,私钥也是会泄露的。这时候就需要证书吊销机制。
嵌入式设备上常用的两种方式:
- CRL(证书吊销列表):CA 定期发布一个黑名单,设备下载后缓存。缺点是列表会越来越大。
- OCSP(在线证书状态协议):实时查询某个证书是否有效。缺点是设备需要联网。
我个人更推荐混合方案:设备出厂时预置 CRL 快照,运行时定期增量更新。对于无法联网的设备,至少要做到检查证书有效期——这个很多人都会忘。
3.6 本章知识体系
下面这张图把 PKI、证书链、信任根的关系串起来了。你可以把它当作固件签名安全设计的「地图」:
3.7 实战中的常见陷阱
讲完理论,说几个我踩过的坑:
- 证书链不完整:有些 SDK 只验证叶子证书,不验证中间证书。攻击者可以伪造一个叶子证书,用自己搭的 CA 签。一定要追溯到信任根。
- 时间戳依赖:证书验证依赖系统时间。如果设备没有 RTC 或 RTC 可被篡改,攻击者可以把时间改回证书有效期内。解决方案是用硬件单调计数器或安全时间源。
- 根公钥硬编码位置:不要放在固件镜像的固定偏移处。我见过攻击者通过对比两个版本固件的二进制差异,直接定位到根公钥位置。建议分散存储 + 哈希校验。
- 忽略证书吊销:很多设备出厂后从不更新 CRL。私钥泄露的证书可以一直用。至少要做到出厂时预置最新 CRL,并在 OTA 时附带增量更新。
我的习惯:每次做固件签名方案评审时,我都会问三个问题:1)信任根在哪?2)证书链能追溯到根吗?3)证书吊销怎么处理?这三个问题能筛掉 80% 的设计缺陷。