4. 固件签名原理:非对称加密基础、RSA与ECDSA算法选择、Zephyr签名工具链
好,咱们进入正题。固件签名这事儿,说白了就是给固件贴个「防伪标签」。你想想看,嵌入式设备升级时,怎么知道下载下来的固件是官方发布的,而不是黑客塞进来的恶意代码?答案就是——签名验证。
我个人习惯把签名比作「数字印章」。你盖个章,别人能验真伪,但谁也伪造不了你的章。这个思路,就是非对称加密的核心。
4.1 非对称加密基础:公钥与私钥
非对称加密,名字听着唬人,其实原理很简单。它有两把钥匙:一把私钥,自己藏着;一把公钥,公开出去。
- 私钥签名:你用私钥对固件哈希值加密,生成签名。
- 公钥验签:设备用公钥解密签名,比对哈希值。一致,则固件可信。
我在项目中遇到过最典型的坑:有人把私钥硬编码在代码里,还上传到了GitHub。嗯,那基本等于把家门钥匙贴在了大门口。私钥必须物理隔离,最好用HSM(硬件安全模块)存储。
核心要点:签名不加密固件本身,只加密固件的哈希值。这样既保证完整性,又节省计算资源。
4.2 RSA与ECDSA算法选择
选RSA还是ECDSA?这是每个做安全启动的人都要面对的问题。我直接说结论:新项目优先选ECDSA。
| 对比项 | RSA | ECDSA |
|---|---|---|
| 密钥长度 | 2048位起步 | 256位即可 |
| 签名速度 | 较慢 | 快 |
| 验签速度 | 快 | 较快 |
| 存储占用 | 大(公钥+签名) | 小 |
| 安全性 | 成熟可靠 | 同等安全强度下更优 |
为什么我推荐ECDSA?说白了,嵌入式设备资源有限。ECDSA用更短的密钥达到同等安全强度,省Flash、省RAM、省电量。我曾经在一个Cortex-M0的芯片上做安全启动,Flash总共才64KB。用RSA-2048,光公钥就占了256字节,签名又是256字节。换成ECDSA P-256,公钥才64字节,签名64字节,省出来的空间够放一个bootloader了。
注意:ECDSA依赖高质量的随机数生成器。如果你的设备没有硬件TRNG(真随机数发生器),签名时可能会出问题。我遇到过某款MCU的随机数生成器有缺陷,导致ECDSA签名偶尔失败,排查了两天才找到原因。
4.3 Zephyr签名工具链
Zephyr官方提供了完整的签名工具链,核心是 west sign 命令。它背后调用了 imgtool 和 mcuboot 的签名工具。
基本用法如下:
# 生成密钥对(ECDSA P-256)
west sign -t imgtool -- --key mykey.pem --algo ecdsa-p256
# 对固件签名
west sign -t imgtool -- --key mykey.pem --algo ecdsa-p256 \
--hex build/zephyr/zephyr.signed.hex
# 查看签名信息
west sign -t imgtool -- --key mykey.pem --dump build/zephyr/zephyr.signed.hex
这里有个细节:west sign 默认使用 imgtool 后端,它支持RSA和ECDSA两种算法。我个人习惯在项目根目录放一个 signing_keys 文件夹,里面只放公钥,私钥单独存放在安全位置。
小技巧:开发阶段可以用测试密钥,但生产环境一定要换正式密钥。我曾经见过有人把测试密钥直接部署到量产设备上,结果安全启动形同虚设。
4.4 签名流程实战
完整的签名流程,我总结为三步:
- 生成密钥对:
openssl ecparam -name prime256v1 -genkey -out private.pem - 提取公钥:
openssl ec -in private.pem -pubout -out public.pem - 签名固件:
west sign -t imgtool -- --key private.pem --algo ecdsa-p256
签名后的固件,头部会附加一个 签名结构体,包含:
- 固件哈希值(SHA-256)
- 签名值(ECDSA或RSA)
- 密钥索引(支持多密钥轮换)
设备启动时,bootloader会做以下验证:
- 读取签名头部
- 计算固件哈希
- 用公钥验签
- 通过则启动,失败则回滚
避坑指南:我曾经在签名时忘记指定 --algo 参数,结果工具默认用了RSA-2048,而bootloader配置的是ECDSA。设备启动时验签失败,直接变砖。嗯,从那以后我每次签名都会检查算法一致性。
4.5 多密钥支持与轮换
Zephyr的签名工具链支持多密钥轮换。什么意思?就是你可以预置多个公钥在bootloader里,签名时指定用哪个密钥。万一某个私钥泄露,可以切换到备用密钥,不用重新烧录bootloader。
配置方法:
# 生成多个密钥对
openssl ecparam -name prime256v1 -genkey -out key0.pem
openssl ecparam -name prime256v1 -genkey -out key1.pem
# 签名时指定密钥索引
west sign -t imgtool -- --key key0.pem --algo ecdsa-p256 --slot-size 0x100000
west sign -t imgtool -- --key key1.pem --algo ecdsa-p256 --slot-size 0x100000
bootloader里需要配置 CONFIG_BOOT_SIGNATURE_KEY_COUNT 和对应的公钥数组。我个人建议至少预置2-3个密钥,以备不时之需。
重要:密钥轮换不是万能的。如果攻击者拿到了你的私钥,他可以用旧密钥签名恶意固件。所以,密钥管理才是安全启动的核心。我见过最离谱的情况:有人把私钥放在U盘里,U盘丢了...嗯,后果可想而知。
4.6 小结
固件签名这事儿,说难不难,说简单也不简单。核心就三点:
- 用非对称加密保证固件来源可信
- ECDSA在嵌入式场景下优于RSA
- Zephyr的
west sign工具链开箱即用
但工具只是工具,真正决定安全性的,是人的意识。私钥保管、算法选择、密钥轮换策略,每一个环节都不能马虎。我做了这么多年嵌入式安全,最大的感悟就是:安全不是加个签名就完事了,而是一个持续的过程。
下一章,咱们聊聊如何把签名验证集成到MCUboot里,以及怎么处理签名失败的回滚逻辑。到时候我会分享一个我踩过的坑——签名验证通过了,但固件还是跑不起来,你猜是为什么?