为什么要做安全启动
没有安全启动的 MCU,任何人用调试器就能把你的固件dump出来、改两行、再烧回去。智能家居被别人控制、门锁被复制、充电桩被刷成任意程序——这些都是真实发生过的事。安全启动要解决的问题就一个:让设备上电后,只跑你签过名的固件,别的一律不跑。
安全的根在硬件里。密钥不能存在 Flash 里,必须放在芯片内部的 eFuse 或 OTP,软件读不出来、也改不掉。
这篇讲怎么把 MCU 的 Secure Boot 真正用起来,配合固件签名和 TEE,做一台"出厂即安全"的设备。
信任链:从 ROM 到 App
信任链(Chain of Trust)的思路:芯片出厂时,ROM 里固化一段不可改的 BootROM。它上电第一件事就是校验下一级(Bootloader)的签名,通过了才跳转;Bootloader 再校验 App 的签名,通过了才跳转。任何一级签名不对,设备进不了正常模式。
信任链层级
- ROM(根)出厂固化,存公钥 hash,不可改。
- Bootloader厂家烧录,校验 App。
- App用户固件,被 Bootloader 校验。
- OTA 镜像升级包,Bootloader 校验后写入。
固件签名怎么做
工程上常用 ECDSA(P-256)签名固件。流程:开发者本地有私钥,编译固件后算 SHA-256 摘要,用私钥签摘要;设备端存公钥,验签通过才跑。
# 签名端(PC 上)
# 1. 算固件摘要
sha256sum app.bin > app.hash
# 2. 用私钥签名
openssl pkeyutl -sign -in app.hash -inkey priv.pem -out app.sig
# 3. 打包:app.bin + app.sig + 元信息(版本、长度)
# 设备端 Bootloader
verify_signature(app_bin, app_len, app_sig, public_key);
if (!ok) { enter_recovery(); }
jump_to_app();
私钥必须离线保管,泄漏一次整批设备全废。公钥 hash 烧进 eFuse,永远不能改。
MCU 上的 Secure Boot
主流 MCU 都自带 Secure Boot 硬件:
- STM32L5/U5/H5内置 RoT,支持 ECDSA 验签。
- NXP i.MX RTSROM + CSF 签名链。
- ESP32Secure Boot V2,AES 加密分区。
- GD32W515国产,TrustZone-M。
用法:按芯片手册把公钥 hash 烧进 OTP,再熔"永久禁止回退到普通启动"的熔丝。这一步不可逆,务必测试充分再量产。
TEE 与密钥管理
安全启动管"能不能跑",TEE(可信执行环境)管"运行时怎么保护密钥"。Cortex-M 有 TrustZone-M,把芯片分成安全世界和普通世界:WiFi 密码、设备私钥放在安全世界,Linux/RTOS 在普通世界,通过 SMC 调用才能取密钥。
密钥存放位置
- 根公钥 hasheFuse/OTP。
- 设备唯一密钥安全世界 flash,加密存储。
- 会话密钥RAM,断电即失。
- WiFi 密码安全世界,不暴露给应用。
OTA 升级中的安全
OTA 是最大的攻击面。安全 OTA 的做法:
- 升级包必须带签名,Bootloader 验签通过才写入。
- 双分区:A/B 两套固件,新固件刷到 B 分区,启动失败自动回滚到 A。
- 加密传输:HTTPS 或 MQTT over TLS。
- 版本号单调递增,防止攻击者回滚到老漏洞版本。
常见攻击与对策
// 1. 调试口读取:量产前把 SWD/JTAG 永久关闭
// 2. 电压毛刺攻击:选带防毛刺的芯片,软件加冗余校验
// 3. 侧信道(功耗分析):加随机延时、混淆运算
// 4. 物理拆片:关键密钥放 eFuse,不依赖 flash 加密
安全是分层的:防住普通爱好者很容易,防住国家级实验室很难。消费级产品做到"普通人搞不动"就够了。
按这套骨架做完,你的设备就和"开源固件随便刷"的玩具拉开了距离。