4、固件签名与打包:imgtool工具使用、生成签名固件、固件版本管理、固件元数据(TLV)结构解析
好,咱们进入第四章。这一章讲的是固件签名与打包,说白了就是给你的固件「上锁」和「贴标签」。我在做OTA项目时,早期吃过没签名的亏——设备被刷了第三方固件,直接变砖。从那以后,我对签名这件事就特别较真。
4.1 imgtool工具:你的固件「身份证」制作器
imgtool是MCUboot官方提供的命令行工具。它负责两件事:给固件签名,以及打包成MCUboot能识别的格式。我个人习惯把它叫做「固件身份证制作器」。
安装很简单,用pip就能搞定:
pip install imgtool
装好后,你可以用 imgtool version 检查是否成功。嗯,这里要注意,Python版本建议3.8以上,我遇到过3.6版本下某些TLV字段解析异常的问题。
4.2 生成签名固件:三步走
签名固件的生成,我总结为三步:准备密钥、签名、验证。咱们一步步来。
4.2.1 准备密钥
首先,你得有一对公私钥。私钥自己保管,公钥要烧到设备里。生成密钥的命令:
imgtool keygen -k mykey.pem -t rsa-2048
这里我用了RSA-2048,你也可以选ECDSA-P256。我个人更倾向ECDSA,因为签名短,省空间。但有些客户指定要RSA,那就得用RSA。
4.2.2 签名固件
有了密钥,就可以签名了。假设你编译好的固件叫 app.bin:
imgtool sign \
--key mykey.pem \
--align 8 \
--version 1.2.3 \
--slot-size 0x100000 \
--max-align 8 \
--header-size 0x200 \
app.bin signed_app.bin
参数说明:
--align 8:对齐方式,通常和Flash擦除块大小一致--version:固件版本号,这个很重要,后面会细说--slot-size:每个slot的大小,必须和实际Flash分区一致--header-size:头部大小,MCUboot用它来存放元数据
--align 1 试试。但生产环境我建议用8或16,性能更好。
4.2.3 验证签名
签名完,记得验证一下:
imgtool verify signed_app.bin
这个命令会检查签名是否有效,以及TLV结构是否完整。我每次打包完都会跑一遍,确保万无一失。
4.3 固件版本管理:别让版本号成为你的噩梦
版本管理看似简单,但坑不少。MCUboot的版本号格式是 major.minor.patch+0,比如 1.2.3+0。后面的 +0 是build号,可以递增。
为什么版本号重要?因为MCUboot升级时,会检查新固件的版本是否高于当前版本。如果版本号一样或更低,它就不升级。我曾经在测试时,忘了改版本号,结果刷了十几次都没成功,还以为是bug。
我建议的版本管理策略:
- 开发阶段:用
0.x.x,每次提交递增patch - 内部测试:用
1.x.x,每次测试递增build号 - 正式发布:用
2.x.x及以上,严格遵循语义化版本
4.4 固件元数据(TLV)结构解析
TLV是Type-Length-Value的缩写。MCUboot用它来存储签名、哈希、版本等元数据。说白了,就是在固件后面附加一些「小纸条」。
TLV结构长这样:
| 字段 | 大小 | 说明 |
|---|---|---|
| Type | 2字节 | 标识数据类型,比如0x01表示签名 |
| Length | 2字节 | Value的长度 |
| Value | 可变 | 实际数据 |
常见的TLV类型:
0x01:SHA256哈希0x02:RSA签名0x03:ECDSA签名0x10:固件版本号0x11:固件大小
你可以用 imgtool dump 查看固件的TLV结构:
imgtool dump signed_app.bin
输出大概是这样:
TLV block at offset 0x100200:
Type: 0x01 (SHA256) Length: 32
Type: 0x02 (RSA2048) Length: 256
Type: 0x10 (VERSION) Length: 8
你想想看,MCUboot启动时,会先读取固件头部的TLV区域,验证签名和哈希。如果签名不对,直接拒绝启动。这就是安全启动的核心机制。
4.5 实战:完整的签名打包流程
最后,我分享一个我常用的脚本片段。它把编译、签名、打包串起来:
#!/bin/bash
# 编译固件
west build -b nrf52840dk_nrf52840 app
# 签名
imgtool sign \
--key private.pem \
--version 2.0.1 \
--slot-size 0x100000 \
--header-size 0x200 \
build/zephyr/zephyr.bin \
build/signed_firmware.bin
# 验证
imgtool verify build/signed_firmware.bin
echo "签名固件已生成:build/signed_firmware.bin"
这个脚本我用了两年,基本没出过问题。唯一要注意的是,每次发布前记得更新版本号。
好,这一章就到这里。下一章我们会讲固件加密与安全启动,到时候会用到今天签名的知识。记得把密钥保管好,别像我那个同事一样。