4. 签名与校验机制:代码签名、哈希校验与防回滚
各位工程师,咱们接着聊。上一章我们把烧录流程理清了,但光有流程还不够——你怎么保证烧进去的固件是“干净”的?没有被篡改过?版本是不是最新的?
这就是本章要啃的硬骨头:签名、校验、防回滚。这三板斧砍下去,攻击者想塞个假固件进来,基本没戏。
4.1 哈希校验:先给固件打个“指纹”
先说最基础的——哈希校验。说白了,就是给固件算一个唯一的摘要。哪怕你只改了一个 bit,算出来的哈希值都会天差地别。
我个人习惯用 SHA256。为什么?因为 MD5 和 SHA1 已经被证明有碰撞风险了,虽然实际攻击门槛很高,但做产品嘛,安全标准得往前看。
实际项目中,我一般这样用:
- 编译完固件,立刻算 SHA256,得到一个 32 字节的哈希值。
- 把这个哈希值烧到芯片的 OTP(一次性可编程)区域,或者存到安全存储区。
- 启动时,Bootloader 重新计算当前固件的 SHA256,跟存储的值比对。
代码示例很简单,但要注意实现细节:
// 伪代码:Bootloader 中的哈希校验
uint8_t expected_hash[32];
uint8_t computed_hash[32];
// 从安全存储区读取预期哈希
read_secure_storage(EXPECTED_HASH_ADDR, expected_hash, 32);
// 计算当前固件的 SHA256
sha256_start();
sha256_update(firmware_addr, firmware_size);
sha256_finish(computed_hash);
// 比对
if (memcmp(expected_hash, computed_hash, 32) != 0) {
// 校验失败,拒绝启动
enter_error_mode();
}
4.2 代码签名:RSA 与 ECDSA 的选择
哈希校验只能防“无意的损坏”,但防不住“恶意的篡改”。为什么?因为攻击者可以同时替换固件和哈希值。这时候就需要非对称签名了。
签名机制的核心逻辑:
- 私钥签名:在开发环境(PC端)用私钥对固件哈希签名。
- 公钥验签:在设备端用公钥验证签名是否匹配。
公钥是公开的,但私钥必须死死守住。我在项目中见过最惨的案例——有人把私钥直接放到了 Git 仓库里……嗯,那基本等于把家门钥匙挂在门外了。
4.2.1 RSA vs ECDSA:怎么选?
这两个算法各有千秋,我列个表方便你对比:
| 对比项 | RSA (2048位) | ECDSA (P-256) |
|---|---|---|
| 签名长度 | 256 字节 | 64 字节 |
| 验签速度 | 较慢(模幂运算) | 较快(椭圆曲线点乘) |
| 密钥生成 | 慢 | 快 |
| 硬件加速 | 很多 MCU 内置 | 较新 MCU 才支持 |
| 安全性 | 成熟可靠 | 同等强度下密钥更短 |
我个人建议:如果你的 MCU 有硬件 RSA 加速器,用 RSA 2048 就够用了。如果追求签名速度或者存储空间紧张,选 ECDSA P-256。我在一个智能门锁项目里用过 ECDSA,验签一次只要 30ms,比 RSA 快了一倍多。
4.3 签名验证流程:从编译到启动
完整的签名验证流程,我画了一张图,你一看就明白:
你看,整个流程其实不复杂。核心就一句话:私钥签名,公钥验签,哈希做桥梁。
4.4 防回滚机制:为什么需要它?
签名校验保证了固件是“正品”,但没保证是“最新版”。攻击者可以拿一个旧版本的合法固件(比如有已知漏洞的 v1.0)刷进去,然后利用旧版本的漏洞搞事情。
这就是回滚攻击。我见过一个真实案例:某 IoT 设备的老版本固件里有个缓冲区溢出漏洞,攻击者拿到旧固件签名后,强行降级,然后远程执行了恶意代码。
防回滚的常见做法:
- 版本号寄存器:在 OTP 或安全存储区维护一个单调递增的版本号。
- 版本号比对:烧录时检查新固件的版本号必须 ≥ 当前版本号。
- 熔丝/efuse:一旦烧录了某个版本,物理熔断,永远无法回到更低版本。
4.5 综合策略:把三件事串起来
在实际项目中,我通常这样设计安全启动链:
- BootROM(只读):验证 Bootloader 的签名和版本号。
- Bootloader:验证主固件的签名、哈希、版本号。
- 主固件:运行时可以验证应用分区的签名(比如 OTA 升级包)。
每一级都做签名+版本检查,形成一条信任链。攻击者只要攻破任何一级,整个链条就断了——但每一级都很难攻破。
4.6 避坑总结
最后,我把这些年踩过的坑总结一下:
| 坑 | 后果 | 解决方案 |
|---|---|---|
| 私钥泄露 | 攻击者可签名任意固件 | 使用 HSM(硬件安全模块)管理私钥 |
| 随机数不够随机 | ECDSA 私钥泄露 | 每次签名前做熵源检查 |
| 版本号未绑定签名 | 回滚攻击 | 版本号放在签名数据内 |
| 公钥存储在可写区域 | 攻击者可替换公钥 | 公钥烧入 OTP 或 efuse |
| 校验时机太晚 | 攻击者绕过校验 | 在 BootROM 阶段完成校验 |
嗯,这一章的内容就到这儿。签名校验和防回滚,说白了就是“防伪+防旧”。把这两件事做好了,你的设备安全底线就立住了。