3. 升级包制作与签名:差分算法、加密与签名校验
升级包怎么做?说白了就两件事:怎么把新版本变小,以及怎么保证没人篡改它。
我见过不少团队,差分算法跑通了就急着上线,结果升级包被中间人替换,设备变砖。嗯,这一章咱们就把这两件事彻底讲透。
3.1 差分算法:为什么你的包那么大?
全量升级包就是把整个固件重新发一遍。如果固件 100MB,每次升级都发 100MB,流量和耗时都扛不住。
差分算法只计算新旧版本之间的差异。比如你只改了一行代码,差分包可能只有几百 KB。
3.1.1 bsdiff:老牌选手
bsdiff 是 Colin Percival 在 2003 年提出的算法。它的核心思路是:
- 用后缀排序找出新旧文件的最长公共子串
- 只记录新增、删除、修改的部分
- 输出一个二进制补丁文件
我在项目中用过很多次 bsdiff。它的压缩率确实好,但有个痛点——内存占用高。处理 100MB 的固件,内存峰值能到 400MB 以上。低端 MCU 根本跑不动。
3.1.2 hdiffpatch:轻量级替代
hdiffpatch 是 bsdiff 的改良版。它用哈希匹配代替后缀排序,内存占用大幅降低。
| 对比项 | bsdiff | hdiffpatch |
|---|---|---|
| 压缩率 | 更优(通常小 10-20%) | 略差 |
| 内存占用 | 高(约 4x 原文件大小) | 低(约 1.5x 原文件大小) |
| 打补丁速度 | 较慢 | 快 2-3 倍 |
| 适用硬件 | 服务器/PC 端生成 | 嵌入式端生成或解包 |
我个人习惯:服务器端用 bsdiff 生成差分包,设备端用 hdiffpatch 解包。这样既省流量,又不会把设备内存撑爆。
3.1.3 差分流程实战
下面是一个典型的差分包制作流程:
# 服务器端:生成差分包
bsdiff old_firmware.bin new_firmware.bin delta.patch
# 设备端:应用差分包
bspatch old_firmware.bin new_firmware.bin delta.patch
注意:旧固件必须完整保留在设备上。如果旧固件被损坏或擦除,差分包就无法应用。
3.2 升级包加密:别让固件裸奔
差分包虽然小了,但内容还是明文。如果有人抓包拿到你的固件,逆向工程、复制产品、植入后门……想想都头疼。
加密是必须的。常用的方式有两种:
3.2.1 对称加密(AES)
加密和解密用同一个密钥。速度快,适合资源受限的设备。
# 加密差分包
openssl enc -aes-256-cbc -salt -in delta.patch -out delta.enc -K $KEY -iv $IV
# 设备端解密
openssl enc -d -aes-256-cbc -in delta.enc -out delta.patch -K $KEY -iv $IV
密钥怎么存?这是个难题。我见过有人把密钥硬编码在固件里——那跟没加密有什么区别?
我的做法是:密钥由服务器下发,每次升级动态协商。设备用预置的公钥加密一个随机数发给服务器,服务器用私钥解密后,用这个随机数作为 AES 密钥。
3.2.2 非对称加密(RSA/ECC)
公钥加密,私钥解密。或者反过来,私钥签名,公钥验签。
非对称加密慢,不适合加密大文件。所以实际项目中,常用混合加密:
- 用 RSA 加密一个随机生成的 AES 密钥
- 用这个 AES 密钥加密差分包
- 设备先用 RSA 私钥解密出 AES 密钥
- 再用 AES 密钥解密差分包
3.3 签名校验:确保包没被换过
加密只能防偷看,不能防篡改。攻击者可以替换你的加密包,设备解密后照样执行——结果就是变砖。
签名校验才是最后一道防线。
3.3.1 签名流程
- 服务器计算差分包的哈希值(SHA-256)
- 用私钥对哈希值签名
- 将签名附加到加密包后面
- 设备用公钥验签,确认哈希值匹配
- 哈希值匹配后才解密、应用
# 服务器端签名
openssl dgst -sha256 -sign private.pem -out delta.sig delta.enc
# 设备端验签
openssl dgst -sha256 -verify public.pem -signature delta.sig delta.enc
3.3.2 公钥怎么保护?
公钥是公开的,但你不能让攻击者替换它。如果设备里的公钥被换了,攻击者就能用自己的私钥签名恶意包。
我建议:
- 公钥烧死在芯片 OTP 区域(一次性可编程),出厂后不可更改
- 或者使用安全元件(SE),公钥存储在硬件隔离区
- 再或者签名链:用芯片厂商的根证书签你的公钥,设备只信任根证书
3.4 完整升级包结构
把上面所有环节串起来,一个完整的升级包长这样:
升级包结构:
┌─────────────────────────────┐
│ 包头(版本号、固件大小、CRC) │
├─────────────────────────────┤
│ 加密的差分包数据 │
├─────────────────────────────┤
│ 签名数据(SHA-256 + RSA) │
└─────────────────────────────┘
设备端校验顺序:
- 解析包头,检查 CRC
- 用公钥验签,确认包未被篡改
- 解密差分包
- 应用差分,合成新固件
- 校验新固件的完整性
- 写入备用分区,标记启动
3.5 知识体系总览
下面这张图把本章的核心逻辑串起来了:
嗯,到这里,升级包的制作和签名就讲完了。差分算法选型、加密方式选择、签名链设计——每一步都关系到升级的安全性和可靠性。别图省事跳过任何一步,否则迟早要还债。