固件签名与验签:非对称加密原理、签名算法选择(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) ③ 私钥签名哈希值 ④ 打包:固件 + 签名 + 公钥证书 网络传输 / 存储介质 验签端(设备端 Bootloader) ⑤ 接收固件包,提取签名 ⑥ 计算接收到的固件哈希 ⑦ 公钥验签:解密签名得哈希A ⑧ 比较哈希A vs 哈希B ✅ 匹配 → 升级 ❌ 不匹配 → 拒绝 公钥证书通常预置在 Bootloader 中,不可更新 私钥存储在 HSM 或离线安全环境中

流程看着简单,但坑都在细节里。我一个个说。

步骤①:编译与哈希计算

固件编译出来后,先算个哈希。我习惯用 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 收到固件后,做三件事:

  1. 计算接收到的固件哈希(SHA-256)
  2. 用预置的公钥解密签名,得到原始哈希
  3. 比较两个哈希是否一致

一致就放行升级,不一致直接拒绝。注意,公钥必须烧死在 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 反而更快。