3、密码学基础(下):数字签名原理、证书链、公钥基础设施(PKI)在固件验证中的角色
好,咱们接着往下聊。上一节我们把哈希和对称/非对称加密讲透了,这一节要啃的硬骨头是数字签名、证书链,还有PKI。说实话,这部分内容在嵌入式安全启动里,是真正决定「你的固件到底是不是你写的」的关键环节。
我当年第一次接触安全启动时,以为只要把固件加密就万事大吉了。后来被老工程师一句话点醒:「加密是防偷看,签名才是防篡改和防伪造。」嗯,这句话我一直记到现在。
3.1 数字签名:给固件盖个「防伪章」
数字签名说白了,就是给固件盖一个独一无二的电子印章。这个印章别人盖不了,但谁都能验证。
签名过程(私钥签名):
- 对固件做哈希运算,得到摘要(比如SHA-256)
- 用私钥加密这个摘要
- 把加密后的摘要(签名值)附加到固件后面
验证过程(公钥验签):
- 从固件中提取签名值
- 用公钥解密签名值,得到原始摘要
- 对固件本体重新计算哈希
- 比较两个摘要是否一致
核心要点:私钥签名,公钥验签。私钥只有你一个人有,所以签名不可抵赖。公钥谁都能拿,所以谁都能验证。
我在项目中遇到过一种情况:有人把公钥直接硬编码在BootROM里,结果发现公钥被逆向出来了。你想想看,公钥本来就不怕被人知道,但问题是——你怎么保证这个公钥真的是你的?这就引出了证书链。
3.2 证书链:信任的传递
假设你拿到一个固件,里面附带了一个公钥。你用这个公钥验签通过了,但你怎么知道这个公钥不是黑客伪造的?
嗯,这就是证书链要解决的问题。
证书链的结构:
- 根证书(Root CA):自签名,信任的起点。通常由芯片厂商或设备厂商持有。
- 中间证书(Intermediate CA):由根证书签发,用于分担风险。
- 终端实体证书(Leaf Cert):由中间证书签发,直接用于固件签名。
验证流程是这样的:
- 从固件中提取终端证书
- 用中间证书的公钥验证终端证书的签名
- 用根证书的公钥验证中间证书的签名
- 根证书是预置在芯片中的,不可更改
我曾经踩过一个坑:根证书过期了。你可能觉得奇怪,根证书怎么会过期?其实很多芯片厂商的根证书是有有效期的,一旦过期,整个信任链就断了。那次我们紧急召回了一批设备,教训深刻。
避坑指南:我曾经在项目中把根证书的有效期设成了100年,结果被安全审计打回来。标准做法是根证书有效期10-20年,中间证书5年,终端证书1-2年。定期轮换才是正道。
3.3 PKI:整个信任体系的基础设施
PKI(公钥基础设施)不是某个具体算法,而是一整套管理密钥和证书的体系。它包括:
| 组件 | 作用 | 在固件验证中的角色 |
|---|---|---|
| CA(证书颁发机构) | 签发和管理证书 | 签发固件签名证书 |
| RA(注册机构) | 验证申请者身份 | 确认固件开发者身份 |
| 证书库 | 存储和分发证书 | 固件中嵌入证书链 |
| CRL/OCSP | 证书撤销机制 | 吊销泄露的签名密钥 |
在嵌入式场景下,PKI的实现往往需要精简。你想想看,一个MCU可能只有几百KB的Flash,不可能把完整的PKI体系塞进去。所以实际做法是:
- 预置根证书:在芯片出厂时烧录,不可更改
- 固件携带证书链:每次升级时附带中间证书和终端证书
- 离线验证:设备不联网,本地完成证书链校验
个人建议:我习惯在固件中同时保留当前证书和上一代证书,这样在证书轮换期间,新旧固件都能正常验证。这个「双证书缓冲」策略帮我避免了好几次升级失败的事故。
3.4 固件验证中的实际流程
好了,我们把前面所有知识点串起来,看看一个完整的固件安全启动流程是什么样的:
// 伪代码:固件验证流程
int verify_firmware(uint8_t* firmware, uint32_t size) {
// 1. 从固件中提取证书链
CertChain* chain = extract_cert_chain(firmware);
// 2. 验证证书链(从叶子到根)
if (!verify_cert_chain(chain, root_cert)) {
return ERR_CERT_CHAIN; // 证书链验证失败
}
// 3. 从叶子证书中提取公钥
PublicKey* pubkey = get_pubkey_from_cert(chain->leaf);
// 4. 用公钥验证固件签名
if (!verify_signature(firmware, size, pubkey)) {
return ERR_SIGNATURE; // 签名验证失败
}
// 5. 验证通过,允许启动
return SUCCESS;
}
这个流程看起来简单,但实际实现时有很多细节。我记得有一次,证书链的解析顺序搞反了——从根到叶子验证,结果一直报错。后来才发现,验证顺序必须是从叶子到根,因为你要先拿到叶子证书里的公钥,才能去验签固件。
3.5 常见攻击与防御
讲完了原理,咱们聊聊实际中会遇到哪些攻击。我这些年做安全审计,见过不少案例:
- 重放攻击:攻击者截获一个旧版本的合法固件,反复刷入。防御方法是在固件中嵌入版本号或时间戳,签名时一起签进去。
- 中间人攻击:在固件传输过程中替换固件。防御方法就是前面讲的证书链验证,只要根证书安全,中间人无法伪造签名。
- 侧信道攻击:通过功耗、电磁辐射等信息泄露私钥。防御方法是使用恒定时间算法,或者硬件安全模块(HSM)来保护私钥。
特别提醒:我曾经见过一个产品,私钥直接以明文形式存储在编译服务器的环境变量里。结果服务器被入侵,所有固件签名都被伪造了。记住,私钥必须存储在硬件安全模块(HSM)或安全元件(SE)中,绝不能出现在任何文件系统里。
3.6 小结
这一节的内容确实有点多,但都是硬核知识。数字签名解决了「谁签的」问题,证书链解决了「公钥是否可信」的问题,PKI则提供了整套管理框架。这三者缺一不可。
我个人习惯在做安全启动方案时,先把证书链画出来,明确每一级的信任关系。然后再去实现代码。这样思路清晰,不容易出错。
下一节我们会讲具体的硬件安全机制,比如OTP、eFuse、安全存储这些。到时候你会发现,密码学算法和硬件安全是相辅相成的。好,今天就到这里。