4. Secure DFU与签名验证
Legacy DFU 只校验固件包的 CRC,任何拿到 .zip 升级包的人都能改包重签,因此它只能防传输错误,不能防恶意固件。Secure DFU 在 Nordic nRF5 SDK 中引入了一套基于 ECDSA(椭圆曲线数字签名)的信任体系:升级包由私钥签名,设备端只保存公钥,Bootloader 在写入前用公钥验证签名,验证不通过就拒绝升级。本节围绕这条信任链,讲清密钥怎么生成、固件怎么签、公钥怎么进设备、启动时怎么验、以及如何防止把设备刷回旧的有漏洞版本。
一、信任链的整体结构
Secure DFU 的信任根是开发者自己生成的一对 ECDSA 密钥。私钥留在开发/发布环境,用于对固件镜像签名;公钥被编译进 Bootloader,随 Bootloader 一起烧录到设备。设备每次收到升级包,Bootloader 用内置公钥验证包内固件的签名,通过后才写入 Flash 并更新启动信息。这样即使攻击者截获并篡改升级包,也无法伪造出能被公钥验证通过的签名。
二、密钥生成与公钥烧录
Nordic 提供 nrfutil 工具完成密钥与签名操作。生成私钥的命令如下,它会输出一个 PEM 格式的私钥文件:
nrfutil keys generate priv.pem
nrfutil keys display --key pk --format code priv.pem --out_file dfu_public_key.c
第一条命令生成 ECDSA 私钥(默认 secp256r1 曲线),第二条把对应的公钥导出为 C 源码文件 dfu_public_key.c。这个文件必须替换掉 SDK 中 components/libraries/bootloader/dfu/ 目录下的同名文件,然后重新编译 Bootloader。也就是说,公钥不是运行时写入的,而是编译期固化进 Bootloader 镜像的。烧录时用 nrfjprog 或 nRF Connect Programmer 把 SoftDevice、Bootloader、Bootloader Settings 页一起写入,公钥随 Bootloader 一起生效。
三、固件签名与升级包生成
签名发生在打包阶段。nrfutil 会对固件镜像计算哈希,再用私钥对哈希签名,把签名、公钥标识、版本号、固件类型等信息一起写进升级包。典型命令:
nrfutil pkg generate \
--hw-version 52 \
--sd-req 0xCA \
--application-version 3 \
--application app.hex \
--key-file priv.pem \
app_dfu_package.zip
其中 --application-version 3 是本次固件的版本号,它直接参与防回滚判断;--sd-req 指定所依赖的 SoftDevice 版本,Bootloader 会检查设备当前 SoftDevice 是否匹配;--hw-version 用于硬件版本匹配。生成的 zip 包就是通过 BLE 或串口下发给设备的升级包。设备端 Bootloader 收到后,先解包读取 init packet,用内置公钥验证签名,再决定是否写入。
四、防回滚策略
防回滚的核心是版本号比较。Bootloader 在 Bootloader Settings 页中保存了当前已激活固件的版本号。当新包版本号小于或等于当前版本时,Bootloader 会拒绝升级,从而阻止攻击者把设备降级到存在已知漏洞的旧版本。版本号由 --application-version 指定,必须是递增的整数。
需要注意两点:第一,防回滚只比较版本号,不比较内容,所以同一版本号重复刷入也会被拒绝;第二,如果确实需要降级(例如新版本有严重问题需要回退),必须通过调试接口擦除设备并重新烧录,不能走 DFU 通道。此外,Bootloader 自身也有版本,Bootloader 升级同样受签名和版本约束。
| 检查项 | 数据来源 | 不通过的结果 |
|---|---|---|
| 固件签名 | init packet 中的签名 + Bootloader 内置公钥 | 拒绝写入,返回签名错误 |
| 硬件版本 | init packet 的 hw-version 与设备 FICR | 拒绝升级 |
| SoftDevice 依赖 | init packet 的 sd-req 与当前 SD 版本 | 拒绝升级 |
| 版本回滚 | 新包版本号与 Settings 页记录版本号 | 版本不递增则拒绝 |
五、实操中的常见问题
实际调试时,最常见的失败是“签名验证不通过”。原因通常是:公钥文件没有替换、替换后没有重新编译烧录 Bootloader、或者签名时用错了私钥。排查方法是确认设备里 Bootloader 的公钥与签名私钥是同一对。另一个常见问题是升级后设备停在 Bootloader 不跳转,这往往是因为版本号没有递增,被防回滚逻辑拦下。此时用 nRF Connect 连接设备,读取 DFU 服务状态或查看 Bootloader 日志即可确认。
总结来说,Secure DFU 的安全性来自“私钥签名、公钥验证、版本递增”三件事的配合。密钥生成和公钥烧录决定了谁能签出合法固件,签名和验证决定了固件在传输和写入过程中是否被篡改,防回滚则决定了设备能否被降级到不安全版本。三者缺一,信任链就不完整。