3、OTA升级包制作:差分升级包与全量升级包的概念、制作工具与流程、签名与加密
好,咱们进入第三章。这一章聊的是升级包的制作,说白了就是“你要把什么东西发给设备”。
很多刚入行的朋友觉得升级包制作很简单——把新固件打个包,发过去不就完了?嗯,没那么简单。你想想看,一个设备可能只有2MB的Flash空间,新固件却有3MB,你怎么发?或者设备在偏远山区,用2G网络传输,一个10MB的包要传半小时,用户早把电拔了。
所以,升级包制作是OTA里非常关键的一环。我个人习惯把升级包分为两类:全量升级包和差分升级包。咱们一个一个说。
3.1 全量升级包:最笨但最稳的方法
全量升级包,顾名思义,就是把完整的固件镜像打包发给设备。设备收到后,直接擦除旧固件,写入新固件。
优点很明显:
- 实现简单,不需要知道设备当前跑的是什么版本
- 可靠性高,只要传输不出错,升级基本不会失败
- 适合首次烧录或版本跨度很大的升级
缺点也很致命:
- 包体大,占用带宽多
- 升级时间长,用户体验差
- 对设备存储空间要求高(需要同时存放新旧两个固件)
适用场景:
- 设备首次出厂或恢复出厂后的升级
- 版本跨度太大(比如从v1.0直接升到v5.0)
- 设备存储空间充裕,不在乎那点流量
我在一个智能门锁项目里就吃过全量包的亏。当时固件有8MB,设备Flash只有4MB,我天真地以为能直接覆盖升级。结果呢?升级到一半,旧固件被擦除了,新固件还没写完,设备直接变砖。嗯,从那以后我学乖了——全量升级一定要有双备份区,或者确保空间足够。
3.2 差分升级包:省流量、省时间
差分升级包,也叫增量包。它只包含新旧固件之间的差异部分。设备收到后,把差异“打”到旧固件上,生成新固件。
你想想看,如果固件只是改了一行代码,全量包要发几MB,差分包可能只需要几KB。对于物联网设备来说,这省下来的可都是真金白银——流量费、电量、时间。
差分升级的核心原理:
- 用算法对比新旧两个二进制文件
- 找出所有不同的字节块
- 只打包这些差异块,并记录它们在文件中的位置
- 设备端用“补丁”算法,把差异应用到旧固件上
我个人建议:差分升级虽然好,但别滥用。如果版本跨度超过3个主版本,差分包的体积可能比全量包还大。这时候老老实实用全量包吧。
3.3 制作工具与流程
好,理论说完了,咱们来点实际的。差分升级包怎么做?用什么工具?
目前业界主流的差分工具就几个:
| 工具 | 特点 | 适用场景 |
|---|---|---|
| bsdiff/bspatch | 算法成熟,压缩率高,但内存占用大 | Linux/Android设备,固件较大 |
| hdiffpatch | 腾讯出品,支持多平台,速度较快 | 嵌入式设备,资源受限场景 |
| xdelta | 基于VCDIFF标准,简单轻量 | 小固件,快速原型开发 |
| 自研差分算法 | 针对特定硬件优化,但开发成本高 | 芯片原厂或大厂深度定制 |
制作流程(以bsdiff为例):
- 准备旧固件(old.bin)和新固件(new.bin)
- 运行命令:
bsdiff old.bin new.bin patch.bin - 得到差分文件patch.bin
- 对patch.bin进行压缩(可选,推荐gzip或lzma)
- 对压缩后的文件进行签名和加密(后面会讲)
- 打包成OTA升级包格式(通常是tar或自定义格式)
避坑指南:我曾经在一个项目里直接用bsdiff对比两个不同编译器编译出来的固件,结果差分包比全量包还大。为什么?因为编译器版本不同,导致二进制布局全变了。所以记住:差分升级要求新旧固件用完全相同的编译环境,否则差分算法会认为“全都不一样”。
3.4 签名与加密:别让你的包被篡改
升级包做好了,直接发出去?不行。你想想看,如果升级包在传输过程中被中间人篡改了,设备刷了一个恶意固件,后果是什么?轻则设备变砖,重则整个网络被攻破。
所以,签名和加密是OTA升级的安全底线。
签名 vs 加密,有什么区别?
- 签名:保证升级包的完整性和来源可信。设备收到包后,验证签名是否匹配。如果不匹配,说明包被篡改过,拒绝升级。
- 加密:保证升级包的机密性。防止别人抓包分析你的固件内容,窃取你的知识产权。
在实际项目中,我通常的做法是:先签名,再加密。或者只签名不加密(如果固件本身不敏感)。但绝对不要只加密不签名——加密只能防窥探,防不了篡改。
签名流程:
- 生成一对公私钥(RSA 2048位或ECC 256位)
- 用私钥对升级包的哈希值进行签名
- 将签名附加到升级包中
- 设备端用公钥验证签名
加密流程:
- 生成一个随机的对称密钥(AES-128或256)
- 用这个对称密钥加密升级包
- 用设备的公钥加密这个对称密钥
- 将加密后的密钥和加密后的升级包一起发送
- 设备用私钥解密出对称密钥,再用对称密钥解密升级包
重要提醒:私钥一定要保管好!我曾经见过一个团队把私钥直接放在Git仓库里,结果被离职员工下载了。后来那批设备全部需要召回重新烧录公钥。血的教训啊。
3.5 完整的升级包制作流程
好,咱们把上面所有步骤串起来,看看一个完整的升级包是怎么诞生的:
# 1. 生成差分包
bsdiff old_firmware.bin new_firmware.bin delta.bin
# 2. 压缩差分包
gzip -9 delta.bin
# 3. 计算哈希
sha256sum delta.bin.gz > delta.bin.gz.sha256
# 4. 用私钥签名
openssl dgst -sha256 -sign private_key.pem -out delta.bin.gz.sig delta.bin.gz
# 5. 生成随机AES密钥
openssl rand -out aes_key.bin 32
# 6. 用AES密钥加密升级包
openssl enc -aes-256-cbc -in delta.bin.gz -out delta.bin.gz.enc -pass file:aes_key.bin
# 7. 用设备公钥加密AES密钥
openssl rsautl -encrypt -inkey device_public_key.pem -pubin -in aes_key.bin -out aes_key.enc
# 8. 打包成最终OTA包
tar -cvf ota_package.tar delta.bin.gz.enc aes_key.enc delta.bin.gz.sig manifest.json
你看,一个完整的升级包包含了:加密后的差分数据、加密后的密钥、签名文件、以及描述版本信息的manifest.json。
小技巧:manifest.json里除了版本号、硬件型号、包大小这些基本信息外,我建议再加一个“兼容性检查”字段。比如旧固件的最低版本号。这样设备在升级前可以先检查自己是否满足条件,避免升级失败。
好了,这一章的内容就到这儿。总结一下:全量包简单但费流量,差分包省流量但实现复杂。签名保证安全,加密保护隐私。下一章咱们聊聊升级包怎么安全地传到设备上——也就是传输协议的选择。