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。
四、签名与验签流程
下面这张图给出从私钥签名到设备端验签的完整信号链,包含镜像头结构、摘要计算范围与验签判断分支:
五、实操:一次完整的签名与验签
以 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 子命令在主机侧复验一次,确认镜像本身签名有效;若主机侧通过而设备侧失败,再检查设备端公钥存储、版本号策略和摘要范围配置。把"签名侧"和"设备侧"分开验证,能快速缩小问题范围。