4. 安全启动配置与签名机制

安全启动的本质,是让芯片在把控制权交给应用代码之前,先用密码学手段回答一个问题:这段即将运行的镜像,是否由持有私钥的可信方签发,且自签发之后一个字节都没有被改动。Silicon Labs 的 Secure Boot 把这个问题拆成两半:一半是策略配置——决定"验什么、用哪把钥匙验、验不过怎么办";另一半是签名与验签——决定"怎么签、怎么验、验的是哪一段数据"。本节围绕这两半展开,并给出可直接参照的配置与签名流程。

一、安全启动策略:先定规则,再谈签名

在 Secure Boot 中,策略不是运行时可选项,而是固化在芯片安全存储区(如 EFUSE/OTP 或受保护的密钥槽)中的一次性决策。典型策略包含四个维度:

  • 信任根(RoT):以芯片内不可读出的公钥哈希或公钥本身作为起点,后续所有验证都锚定在它上面。
  • 验证范围:只验应用镜像,还是逐级验证 BL2、BL1、应用;范围越大,启动链越完整,但启动耗时和镜像体积也越大。
  • 失败动作:验签失败时是停机(Halt)、进入恢复模式,还是回退到出厂镜像。生产设备通常选择停机或恢复,绝不静默继续。
  • 防回滚:通过单调计数器(Monotonic Counter)记录最低可接受版本号,拒绝旧版本镜像被重新刷入。

策略一旦写入 OTP,通常不可逆。因此工程上先在开发阶段用可擦写的调试配置验证流程,确认无误后再烧录生产策略。一个常见的错误是:先烧了"仅验证应用"的策略,后来想升级为"全链验证",却发现 OTP 已锁死,只能换芯片。

二、公钥与私钥:签名方案的分工

签名采用非对称密码体制。私钥由签名方(通常是研发或发布服务器)保管,用于对镜像生成签名;公钥随镜像或预置在设备中,用于验签。私钥泄露等于信任崩塌,因此私钥应存放在 HSM 或至少是受控的签名服务器中,绝不进入代码仓库。

Silicon Labs 的签名工具(如 commander 的 convert 与 sign 子命令,或 Simplicity Studio 中的 Secure Boot 配置)会读取私钥、计算镜像摘要、生成签名,并把签名与公钥信息打包进镜像头。设备端 BootROM 或 BL2 读取该头部,用公钥验证签名,再决定是否跳转。

三、ECDSA 与 RSA:选哪个

两者都是成熟的数字签名算法,但在嵌入式安全启动场景下差异明显:

维度ECDSA(P-256)RSA(2048/3072)
密钥长度256 位2048~3072 位
签名长度约 64 字节256~384 字节
验签速度较快较慢,密钥越长越明显
签名速度较快慢
镜像开销小大
硬件加速支持多数 EFR32 系列支持部分型号支持

对于 EFR32 这类资源受限的无线 SoC,ECDSA P-256 通常是默认推荐:签名短、验签快、镜像膨胀小。RSA 的优势在于生态兼容性好、部分旧工具链只支持 RSA,若产品需要与既有 PKI 体系对接,可保留 RSA 选项。无论选哪种,摘要算法建议使用 SHA-256,避免 SHA-1。

四、签名与验签流程

下面这张图给出从私钥签名到设备端验签的完整信号链,包含镜像头结构、摘要计算范围与验签判断分支:

签名侧(发布服务器 / HSM) 应用镜像 firmware.bin SHA-256 摘要 32 字节哈希 私钥签名 ECDSA P-256 签名镜像 镜像 + 签名 + 公钥 镜像头结构 Magic / 版本号 4 字节 + 4 字节 公钥 / 公钥哈希 64 字节或 32 字节 签名值 ECDSA: 64 字节 镜像长度 / 偏移 4 字节 + 4 字节 防回滚版本号 4 字节 保留 / 对齐填充 按需 设备侧(BootROM / BL2) 读取镜像头 解析公钥与签名 计算镜像摘要 SHA-256 公钥验签 ECDSA Verify 版本号比对 防回滚检查 验签通过 跳转执行 验签失败 停机 / 恢复 OK FAIL 关键约束 • 私钥永不进入设备,仅用于签名侧;设备只持有公钥或公钥哈希。 • 摘要计算范围必须与签名时完全一致,任何字节差异都会导致验签失败。 • 防回滚版本号必须单调递增,旧版本镜像即使签名有效也应被拒绝。

五、实操:一次完整的签名与验签

以 EFR32 平台为例,假设已有应用镜像 app.bin,私钥为 ecdsa_p256.pem,公钥为 ecdsa_p256_pub.pem。签名过程大致如下(命令为示意,具体参数以所用 SDK 版本为准):

# 1. 生成 ECDSA P-256 密钥对
openssl ecparam -name prime256v1 -genkey -noout -out ecdsa_p256.pem
openssl ec -in ecdsa_p256.pem -pubout -out ecdsa_p256_pub.pem

# 2. 对镜像计算 SHA-256 摘要
openssl dgst -sha256 -binary app.bin > app.sha256

# 3. 用私钥对摘要签名
openssl pkeyutl -sign -inkey ecdsa_p256.pem \
  -in app.sha256 -out app.sig -rawin -digest sha256

# 4. 将签名、公钥、版本号打包进镜像头
commander convert app.bin \
  --secureboot \
  --signkey ecdsa_p256.pem \
  --pubkey ecdsa_p256_pub.pem \
  --version 0x00010002 \
  --out app_signed.bin

设备端验签时,BootROM 或 BL2 会执行与签名侧对称的操作:读取镜像头中的公钥和签名,对镜像体重新计算 SHA-256,再用公钥验证签名。若使用 ECDSA,验签本质是检查签名点是否满足椭圆曲线方程;若使用 RSA,则是用公钥做模幂运算后比对填充格式。两者最终都归结为一个布尔判断。

调试建议:首次配置安全启动时,先用 --version 0x00000001 和可擦写的调试密钥跑通全流程,确认设备能正常启动且日志中无验签错误,再切换到生产密钥并烧录 OTP 策略。这样可以在不锁死芯片的前提下定位问题。

六、结果判读与常见失败原因

验签成功时,设备正常跳转到应用入口,串口或调试接口通常会输出启动阶段信息;验签失败时,根据策略不同,设备可能停在 BootROM、反复重启或进入恢复模式。常见失败原因包括:

  • 摘要范围不一致:签名时对 app.bin 计算,验签时却把镜像头也纳入计算,导致哈希不同。
  • 公钥不匹配:设备中预置的公钥哈希与镜像头携带的公钥不一致,常见于换密钥后未同步更新设备端。
  • 版本号回退:新镜像版本号低于单调计数器当前值,被防回滚机制拒绝。
  • 签名格式错误:ECDSA 签名在 DER 与原始 r||s 格式之间转换时出错,导致验签失败。

排查时,优先用签名工具自带的 verify 子命令在主机侧复验一次,确认镜像本身签名有效;若主机侧通过而设备侧失败,再检查设备端公钥存储、版本号策略和摘要范围配置。把"签名侧"和"设备侧"分开验证,能快速缩小问题范围。

核心结论:安全启动策略决定信任边界,ECDSA/RSA 决定签名强度与开销,而签名与验签流程的每一个字节范围、每一个版本号都必须严格对齐。三者缺一不可,任何一处偏差都会让整条信任链失效。