固件签名与验签:非对称加密原理、签名算法选择(RSA/ECDSA)、签名流程设计
各位同学,咱们今天聊固件签名。说实话,这是整个安全升级里最核心的一环。没有签名,你前面做的加密、校验都是白搭。我见过太多项目,固件里啥保护都没做,直接裸奔,结果被人在升级包里塞了个后门——嗯,那场面,相当惨烈。
非对称加密:一把钥匙开不了两把锁
先说说非对称加密。你想想看,对称加密就像你家大门钥匙,开锁和锁门用的是同一把。但非对称加密不一样——它有两把钥匙:一把公开(公钥),一把私藏(私钥)。
公钥加密的数据,只有私钥能解开。反过来,私钥签名的数据,公钥能验证。这就是签名验签的数学基础。
我个人习惯把公钥比作「锁」,私钥比作「签名笔」。公钥到处发,谁都能拿它来「锁」数据;但只有你手里的私钥,才能「签」出有效的名字。
核心原则:私钥永远不出设备,公钥可以公开分发。一旦私钥泄露,整个信任体系就崩塌了。
签名算法选择:RSA vs ECDSA
现在主流的选择就两个:RSA 和 ECDSA。我两个都用过,各有各的脾气。
| 对比维度 | RSA | ECDSA |
|---|---|---|
| 密钥长度 | 2048/4096 位 | 256/384 位(等效安全强度) |
| 签名速度 | 慢(私钥操作) | 快(私钥操作) |
| 验签速度 | 快(公钥操作) | 慢(公钥操作) |
| 签名体积 | 256/512 字节 | 64/72 字节 |
| 硬件支持 | 几乎所有 MCU 都有硬件加速 | 较新芯片才支持 |
| 安全性 | 成熟,但量子计算威胁大 | 同等强度下密钥更短,抗量子稍好 |
我在一个物联网网关项目里用过 RSA-2048。当时选它,主要是因为那颗 STM32F4 有硬件 RSA 加速器,验签一次只要几毫秒。后来换到低功耗芯片,硬件不支持 RSA,只能上 ECDSA——虽然验签慢了点,但签名体积小,传输省带宽。
我的建议:如果芯片有硬件 RSA 加速,优先 RSA-2048。如果追求签名体积小、或者芯片资源紧张,选 ECDSA with P-256。别用 1024 位 RSA,那已经不安全了。
签名流程设计:从编译到烧录
流程设计其实不复杂,但细节决定成败。我画了张图,你看一眼就明白了。
流程看着简单,但坑都在细节里。我一个个说。
步骤①:编译与哈希计算
固件编译出来后,先算个哈希。我习惯用 SHA-256,别用 MD5 或 SHA-1,那俩已经破得跟筛子似的。哈希值就是固件的「指纹」,哪怕改一个 bit,指纹都会变。
步骤②:私钥签名
用私钥对哈希值做签名运算。RSA 的话,就是对哈希做私钥加密;ECDSA 则是生成一对 (r, s) 值。签名结果一般 256 字节(RSA-2048)或 64 字节(ECDSA P-256)。
我曾经踩过的坑:签名前一定要确认哈希算法和签名算法匹配。比如 RSA-PSS 需要指定哈希算法,默认是 SHA-1,你得手动改成 SHA-256。否则签名能过,但安全强度直接降级。
步骤③:打包与传输
固件、签名、公钥证书(可选)打包成一个升级文件。我建议把签名放在固件末尾,这样 Bootloader 可以流式处理——先读固件算哈希,读到末尾拿签名验签,不用等整个文件加载完。
步骤④:设备端验签
Bootloader 收到固件后,做三件事:
- 计算接收到的固件哈希(SHA-256)
- 用预置的公钥解密签名,得到原始哈希
- 比较两个哈希是否一致
一致就放行升级,不一致直接拒绝。注意,公钥必须烧死在 Bootloader 里,不能从外部传入——否则攻击者可以替换公钥,用他自己的私钥签名恶意固件。
避坑指南:我踩过的几个雷
- 公钥存储位置:别把公钥放在固件里。我见过有人把公钥写在固件头部,结果攻击者直接替换公钥和签名,轻松绕过验签。公钥必须焊死在 Bootloader 的只读区域。
- 签名算法填充模式:RSA 有 PKCS#1 v1.5 和 PSS 两种填充。PSS 更安全,但有些老芯片不支持。我建议能用 PSS 就用 PSS,实在不行再用 v1.5。
- 哈希长度:ECDSA 签名时,哈希长度必须和曲线阶的比特长度一致。P-256 曲线用 SHA-256,P-384 用 SHA-384。长度不匹配会导致验签失败。
- 防回滚:签名只保证固件来源可信,不保证版本新旧。你得在固件头里加版本号,Bootloader 验签通过后还要比较版本号,防止攻击者拿旧版本固件降级攻击。
代码示例:OpenSSL 命令行签名与验签
我平时开发阶段用 OpenSSL 做签名验证,方便调试。生产环境会用 HSM 或安全芯片管理私钥。下面是个典型流程:
# 1. 生成 RSA 私钥(2048位)
openssl genrsa -out private.pem 2048
# 2. 提取公钥
openssl rsa -in private.pem -pubout -out public.pem
# 3. 计算固件哈希并签名
openssl dgst -sha256 -sign private.pem -out firmware.sig firmware.bin
# 4. 验签(设备端模拟)
openssl dgst -sha256 -verify public.pem -signature firmware.sig firmware.bin
# 如果输出 "Verified OK",说明签名有效
ECDSA 的流程类似,只是生成密钥时指定曲线:
# 生成 ECDSA P-256 私钥
openssl ecparam -genkey -name prime256v1 -out ecdsa_private.pem
# 提取公钥
openssl ec -in ecdsa_private.pem -pubout -out ecdsa_public.pem
# 签名与验签命令同上(OpenSSL 自动识别算法)
小技巧:开发阶段可以用 OpenSSL 的 -engine 参数对接软 HSM,模拟生产环境。我习惯在 CI/CD 流水线里集成签名步骤,每次构建自动签名,避免人工操作泄露私钥。
性能优化:验签不能拖慢启动
设备启动时验签,时间长了用户会骂娘。我优化过几个方案:
- 硬件加速:优先选带硬件 RSA/ECC 加速器的芯片。STM32H7 的硬件 RSA 验签 2048 位只要 2ms,软件实现要 200ms。
- 流式验签:边接收固件边算哈希,不用等全部下载完。Bootloader 维护一个 SHA-256 上下文,每收到 1KB 数据就更新一次。
- 缓存公钥:公钥验签前先做一次有效性检查(比如证书链验证),之后把公钥缓存在 RAM 里,避免每次都解析证书。
- 选择合适曲线:如果芯片没有硬件 ECC,ECDSA 验签可能比 RSA 慢 5-10 倍。这时候选 RSA 反而更快。