3. 升级包制作与签名:全量、差分、签名与版本管理

升级包是OTA的“弹药”。弹药造不好,仗就没法打。这一章,我带你亲手造出合格的升级包。

很多人觉得升级包不就是把固件打个包吗?其实没那么简单。全量包怎么做?差分包怎么算?签名怎么签才不会在设备上校验失败?版本号怎么管才能让OTA服务器认账?这些坑,我一个一个说清楚。

3.1 全量升级包制作

全量升级包,说白了就是把整个系统镜像打包成一个文件。设备拿到它,直接刷写整个分区。

适用场景:

  • 首次烧录或恢复出厂后的首次OTA
  • 版本跨度太大,差分包算不出来
  • 设备变砖后的强制恢复

制作流程:

  1. 编译生成目标固件镜像(如 system.img、boot.img)
  2. 将镜像文件按分区顺序拼接
  3. 添加头部信息(魔数、版本号、镜像大小、校验和)
  4. 计算整体哈希值,写入尾部
  5. 打包成 .zip 或 .swu 格式

关键点:全量包必须包含完整的文件系统。我见过有人只打包了应用层,结果设备刷完内核还是旧的,直接起不来。全量,就是全部,别偷懒。

# 一个简单的全量包制作脚本片段
#!/bin/bash
IMG_DIR="./out/target/product/device/"
OUTPUT="./ota_full_v1.0.zip"

# 打包镜像
zip -j $OUTPUT $IMG_DIR/boot.img $IMG_DIR/system.img $IMG_DIR/vendor.img

# 计算哈希
sha256sum $OUTPUT > $OUTPUT.sha256
echo "全量包制作完成: $OUTPUT"

3.2 差分升级包制作

差分包只包含新旧版本之间的差异。设备只需要下载变化的部分,体积小、速度快。

我刚开始做差分时,总觉得算法是黑盒,跑就完了。直到有一次差分包在设备上解压失败,我才发现——源版本必须和设备当前版本完全一致,差一个字节都不行。

常用差分算法:

算法 特点 适用场景
bsdiff 二进制级别差分,压缩率高 固件镜像、大文件
hdiffpatch 支持内存映射,速度快 嵌入式设备、资源受限环境
zchunk 基于内容分块,支持断点续传 网络不稳定场景

制作步骤:

  1. 准备旧版本镜像(old.img)和新版本镜像(new.img)
  2. 运行差分工具生成补丁文件(patch.diff)
  3. 将补丁文件与元数据打包
  4. 签名并发布

我的经验:差分包越小越好,但别为了压缩牺牲可靠性。我曾经把差分率压到5%,结果设备上解压花了10分钟,用户以为死机了。平衡一下,一般10%-30%的差分率比较合理。

# 使用 bsdiff 制作差分包
bsdiff old_firmware.bin new_firmware.bin upgrade.patch

# 打包差分包
zip delta_update.zip upgrade.patch manifest.json

3.3 数字签名与校验

签名是OTA的安全底线。没有签名的升级包,就像没锁的门——谁都能进。

签名流程:

  1. 生成密钥对(私钥签名,公钥验证)
  2. 计算升级包的哈希值
  3. 用私钥加密哈希值,生成签名
  4. 将签名附加到升级包中

校验流程(设备端):

  1. 提取升级包中的签名
  2. 用公钥解密签名,得到原始哈希
  3. 重新计算升级包的哈希
  4. 比对两个哈希值

注意:私钥必须妥善保管!我曾经见过有人把私钥直接放在代码仓库里,结果被第三方拿到了,伪造了升级包推送给了所有设备。那次事故之后,我们团队专门上了硬件安全模块(HSM)来存私钥。

# 使用 OpenSSL 签名
# 生成私钥
openssl genrsa -out private.pem 2048

# 生成公钥
openssl rsa -in private.pem -pubout -out public.pem

# 计算升级包哈希并签名
openssl dgst -sha256 -sign private.pem -out ota_update.bin.sig ota_update.bin

# 设备端校验
openssl dgst -sha256 -verify public.pem -signature ota_update.bin.sig ota_update.bin

3.4 升级包版本管理

版本管理混乱,OTA就会变成灾难。你想想看,设备A是v1.0,设备B是v1.1,服务器推了个v1.2的差分包,设备A解压失败——因为差分包是基于v1.1算的。

版本号规范:

字段 格式 示例
主版本 大功能变更 v1 → v2
次版本 功能新增 v1.0 → v1.1
修订号 Bug修复 v1.0.1 → v1.0.2
构建号 每日构建 v1.0.1.20250315

版本管理要点:

  • 版本号必须嵌入升级包头部,设备端和服务器端都能读取
  • 维护版本兼容性矩阵:哪些旧版本可以升级到当前版本
  • 差分包必须记录源版本和目标版本
  • 服务器端做版本校验:不允许降级(除非强制模式)

避坑指南:我曾经遇到过版本号用字符串比较的坑——"v1.9" > "v1.10" 居然返回true,因为字符串比较是按字符逐位比的。后来统一改用整数或语义化版本库,再也没出过问题。

3.5 本章知识结构图

下面这张图把升级包制作与签名的核心流程串起来了。你可以把它当作操作手册的目录。

升级包制作与签名核心流程 全量升级包 差分升级包 数字签名 完整镜像 分区打包 哈希校验 bsdiff hdiffpatch 补丁文件 私钥签名 公钥校验 哈希比对 版本管理 版本号规范 兼容性矩阵 降级防护 可发布的OTA升级包