2、固件签名基础:非对称加密原理、哈希算法、数字签名生成与验证流程
好,咱们正式开始聊固件签名。说实话,这是整个可信启动链条里最核心的一环。你想想看,如果没有签名机制,那所谓的「安全启动」就是个空壳子——谁都能往你的 Flash 里写一段恶意代码,然后设备照跑不误。
我刚开始做嵌入式安全那会儿,也踩过不少坑。有一次给一款 IoT 网关做固件升级,没做签名校验,结果客户反馈设备频繁变砖。后来一查,是有人在传输过程中篡改了固件包。嗯,从那以后,我再也不敢跳过签名这一步了。
这一章,咱们就把签名背后的三个基础概念掰开揉碎讲清楚:非对称加密、哈希算法、以及数字签名的生成与验证流程。别怕数学,咱们用工程思维来理解它。
2.1 非对称加密:公钥与私钥的「锁与钥匙」
非对称加密,说白了就是两把钥匙:一把公开(公钥),一把私藏(私钥)。公钥加密的数据,只有私钥能解开;反过来,私钥签名的数据,只有公钥能验证。
我习惯用一个比喻来理解:私钥是你的个人印章,公钥是印鉴样本。你拿印章盖个戳(签名),别人拿印鉴样本对照(验证),就知道这个戳是不是你盖的。
常见的非对称算法有 RSA 和 ECC。咱们做固件签名,选哪个?看场景。
| 算法 | 密钥长度(推荐) | 签名长度 | 计算开销 | 典型应用 |
|---|---|---|---|---|
| RSA | 2048 / 4096 bit | 与密钥长度相同 | 较高(尤其私钥操作) | PC、服务器、高性能 MCU |
| ECDSA | 256 / 384 bit | 约 64 / 96 字节 | 较低 | 嵌入式、IoT、资源受限设备 |
我个人更倾向 ECDSA,尤其是在资源受限的 MCU 上。签名短、算得快,而且同等安全强度下密钥长度比 RSA 短得多。你想想看,一个 256 位的 ECC 密钥,安全性和 3072 位的 RSA 差不多,但签名体积小了好几倍——这对固件包大小敏感的场景来说,太重要了。
2.2 哈希算法:固件的「数字指纹」
非对称加密有个问题:它处理不了大块数据。固件动辄几百 KB 甚至几 MB,用私钥直接对整个固件做签名,计算量巨大,而且签名结果也会和固件一样长——这显然不现实。
所以我们需要哈希算法。哈希函数能把任意长度的数据,压缩成一个固定长度的摘要(digest)。这个摘要就像固件的「数字指纹」:只要固件内容变了哪怕一个 bit,指纹就会完全不一样。
常用的哈希算法有 SHA-256 和 SHA-384。我建议至少用 SHA-256,SHA-1 已经不安全了,别碰。
实际项目中,我们不会对整个固件直接签名,而是:
- 对固件计算哈希值(比如 SHA-256)
- 对哈希值进行签名(用私钥加密哈希值)
- 把签名结果附加到固件包中
验证时,接收方用公钥解密签名得到哈希值,再自己算一遍固件的哈希,两者对比。一致,则固件未被篡改;不一致,则拒绝启动。
2.3 数字签名生成与验证流程
好,现在我们把上面两个概念串起来,看看完整的签名流程长什么样。
下面这张图是我自己画的一个流程框架,你可以把它当作签名操作的「标准操作程序」。
流程其实不复杂,但有几个细节我特别想强调一下。
2.3.1 签名生成阶段(你作为固件发布者)
- 步骤一: 编译出固件二进制文件。嗯,这一步通常 CI/CD 自动完成。
- 步骤二: 对固件计算 SHA-256 哈希。得到 32 字节的摘要。
- 步骤三: 用你的私钥对这个摘要做签名(RSA 或 ECDSA)。输出就是数字签名。
- 步骤四: 把原始固件和签名打包在一起。有些方案还会加上固件版本、设备 ID 等元数据。
2.3.2 签名验证阶段(设备端 BootROM 或 Bootloader)
- 步骤一: BootROM 从 Flash 中读取固件包,分离出固件本体和签名。
- 步骤二: 对固件本体计算 SHA-256 哈希,得到 H1。
- 步骤三: 用预置在芯片 OTP(一次性可编程存储器)中的公钥,解密签名,得到 H2。
- 步骤四: 比较 H1 和 H2。一致则跳转到固件入口执行;不一致则进入错误处理(比如死循环或恢复模式)。
这里有个关键点:公钥是怎么安全地放到芯片里的? 我习惯的做法是在芯片出厂时,通过安全烧录工具把公钥哈希写入 OTP。BootROM 里硬编码了公钥哈希的校验逻辑,确保只有经过授权的公钥才能被使用。这样即使攻击者物理接触了芯片,也无法替换公钥。
2.4 实际工程中的常见坑
讲完了理论,咱们聊聊实战中容易翻车的地方。我列几个自己踩过的坑,你遇到了可以少走弯路。
| 坑 | 现象 | 根因 | 解决方案 |
|---|---|---|---|
| 哈希计算范围不一致 | 签名验证总失败 | 签名时算的哈希范围(比如包含头部),验证时没包含 | 统一约定:只对固件有效载荷做哈希,头部单独处理 |
| 字节序问题 | 签名验证偶尔失败 | MCU 是小端,签名工具是大端,哈希值字节序反了 | 在签名工具和 BootROM 中显式指定字节序转换 |
| 填充模式不匹配 | RSA 签名验证失败 | 签名用了 PKCS#1 v1.5,验证用了 PSS | 统一使用 PSS(更安全),且确认双方参数一致 |
| 公钥存储位置不当 | 公钥被篡改,恶意固件通过验证 | 公钥放在可写的 Flash 区域 | 公钥必须烧录在 OTP 或 eFuse 中,一次性写入 |
2.5 小结
这一章的内容,说白了就是三句话:
- 非对称加密 提供了「私钥签名,公钥验证」的能力。
- 哈希算法 把大固件压缩成固定长度的摘要,让签名变得可行。
- 数字签名 就是「对哈希值做非对称加密」的结果,验证时对比两个哈希值是否一致。