3. 升级包制作与签名:全量、差分、签名与版本管理
升级包是OTA的“弹药”。弹药造不好,仗就没法打。这一章,我带你亲手造出合格的升级包。
很多人觉得升级包不就是把固件打个包吗?其实没那么简单。全量包怎么做?差分包怎么算?签名怎么签才不会在设备上校验失败?版本号怎么管才能让OTA服务器认账?这些坑,我一个一个说清楚。
3.1 全量升级包制作
全量升级包,说白了就是把整个系统镜像打包成一个文件。设备拿到它,直接刷写整个分区。
适用场景:
- 首次烧录或恢复出厂后的首次OTA
- 版本跨度太大,差分包算不出来
- 设备变砖后的强制恢复
制作流程:
- 编译生成目标固件镜像(如
system.img、boot.img) - 将镜像文件按分区顺序拼接
- 添加头部信息(魔数、版本号、镜像大小、校验和)
- 计算整体哈希值,写入尾部
- 打包成
.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 | 基于内容分块,支持断点续传 | 网络不稳定场景 |
制作步骤:
- 准备旧版本镜像(
old.img)和新版本镜像(new.img) - 运行差分工具生成补丁文件(
patch.diff) - 将补丁文件与元数据打包
- 签名并发布
我的经验:差分包越小越好,但别为了压缩牺牲可靠性。我曾经把差分率压到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的安全底线。没有签名的升级包,就像没锁的门——谁都能进。
签名流程:
- 生成密钥对(私钥签名,公钥验证)
- 计算升级包的哈希值
- 用私钥加密哈希值,生成签名
- 将签名附加到升级包中
校验流程(设备端):
- 提取升级包中的签名
- 用公钥解密签名,得到原始哈希
- 重新计算升级包的哈希
- 比对两个哈希值
注意:私钥必须妥善保管!我曾经见过有人把私钥直接放在代码仓库里,结果被第三方拿到了,伪造了升级包推送给了所有设备。那次事故之后,我们团队专门上了硬件安全模块(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 本章知识结构图
下面这张图把升级包制作与签名的核心流程串起来了。你可以把它当作操作手册的目录。