3、升级包制作与签名:差分升级包、全量升级包、签名与校验机制

升级包怎么做?签名又是什么?很多刚入行的朋友觉得这步就是打个包、签个名,没什么技术含量。其实不然。我在项目里见过因为签名算法选错、差分算法边界没处理好,导致整批设备变砖的案例。今天咱们就把这块彻底讲透。

3.1 全量升级包 vs 差分升级包

先搞清楚两个概念。全量包,就是把完整的固件镜像打包下发。差分包,只包含新旧版本之间的差异部分。

对比项 全量升级包 差分升级包
包体大小 大(完整固件) 小(仅差异部分)
生成复杂度 低(直接打包) 高(需差分算法)
适用场景 首次烧录、版本跨度大 小版本迭代、带宽受限
升级成功率 高(独立镜像) 依赖基线版本一致性
存储要求 高 低

我个人习惯,在项目初期先用全量包跑通流程。等OTA链路稳定了,再切到差分升级。一来降低调试复杂度,二来万一出问题,排查范围小。

3.2 差分升级包制作

差分升级的核心算法,主流的有bsdiff、hdiffpatch、xdelta3。我用的最多的是bsdiff,它在二进制差异检测上表现不错。

基本流程是这样的:

  1. 获取旧版本固件(base.hex)和新版本固件(new.hex)
  2. 运行差分工具,生成patch文件
  3. 将patch文件打包成升级包
  4. 设备端接收patch后,与本地旧固件合并,还原新固件
# 使用 bsdiff 生成差分补丁
bsdiff old_firmware.bin new_firmware.bin upgrade.patch

# 设备端使用 bspatch 还原
bspatch old_firmware.bin new_firmware.bin upgrade.patch
避坑指南: 我曾经遇到过一个坑——差分升级后设备反复重启。查了两天才发现,旧固件在设备上已经被部分擦除了,但差分工具比对的是原始文件。结果patch应用到损坏的旧固件上,还原出来的新固件是坏的。所以,差分升级前一定要校验设备端旧固件的完整性。

3.3 全量升级包制作

全量包就简单多了。说白了就是把编译好的固件镜像,加上头部信息、版本号、校验值,打包成一个文件。

一个标准的全量升级包结构:

+----------------------------+
|  Magic Header (4字节)      |  // 固定魔数,如 0xOTA1
|  Version (4字节)           |  // 固件版本号
|  Image Size (4字节)        |  // 固件大小
|  CRC32/Checksum (4字节)    |  // 校验值
|  Signature (256/512字节)   |  // 数字签名
|  Firmware Data (变长)      |  // 固件本体
+----------------------------+

嗯,这里要注意:头部字段的字节序一定要和MCU的架构匹配。小端CPU和大端CPU混用,读出来的版本号、大小全是错的。我见过有人把ARM的包直接丢给RISC-V设备,结果解析失败。

3.4 签名与校验机制

签名是OTA安全的最后一道防线。没有签名的升级包,就像没锁的门——谁都能往里塞东西。

常用的签名算法:

  • RSA:成熟、广泛支持,但签名体积大、验签慢
  • ECDSA:签名短、性能好,适合资源受限设备
  • Ed25519:现代算法,安全性和性能都优秀

我个人倾向在IoT设备上用ECDSA。签名长度只有64字节左右,验签速度也快。RSA 2048的签名要256字节,对flash紧张的设备不太友好。

签名流程:

  1. 对升级包内容做哈希(SHA256)
  2. 用私钥对哈希值签名
  3. 将签名附加到升级包尾部
  4. 设备端用公钥验签
# 生成密钥对
openssl ecparam -genkey -name prime256v1 -out private.pem
openssl ec -in private.pem -pubout -out public.pem

# 对升级包签名
openssl dgst -sha256 -sign private.pem -out upgrade.sig upgrade.bin

# 设备端验签(伪代码)
if (ecdsa_verify(public_key, sha256_hash, signature) == OK) {
    apply_update();
} else {
    reject_update();
}
经验之谈: 密钥管理比签名算法本身更重要。私钥泄露,再强的算法也没用。我建议把签名私钥放在离线机器上,只在构建服务器上用。公钥烧录在设备Bootloader里,并且要加上防读保护,防止被dump出来。

3.5 升级包制作与签名流程总览

下面这张图,把整个流程串起来了。你想想看,从固件编译完成,到最终设备安全升级,中间经历了哪些环节。

升级包制作与签名流程 固件编译完成 选择升级方式:全量 / 差分 全量打包 + 头部信息 差分算法生成patch SHA256 哈希计算 私钥签名(ECDSA/RSA) 设备端使用预置公钥验签,验签通过后才允许写入固件

3.6 校验机制与安全加固

签名只是校验的一部分。完整的校验链应该包含:

  • 完整性校验:CRC32或SHA256,确保传输过程中没丢包、没篡改
  • 签名校验:验证升级包来源是否可信
  • 版本号校验:防止回滚到有漏洞的旧版本
  • 硬件绑定校验:升级包只能用于指定型号的设备

核心原则: 校验必须在Bootloader阶段执行,不能在应用层做。为什么?因为应用层可能已经被攻击者替换了。Bootloader是信任根,它验签通过,才允许升级。

我记得有一次做安全审计,发现某款设备的签名校验放在应用层。攻击者只要刷一个假的应用程序,就能绕过签名检查,直接写任意固件。这就是典型的信任根没守住。

最后说一句:升级包制作不是一锤子买卖。版本迭代过程中,密钥轮换、算法升级都要提前规划。别等到产品出货了,才发现签名算法太弱、密钥没法更新——那时候就晚了。