4、安全启动(Secure Boot):安全启动原理、信任根(RoT)建立、签名验证流程

各位同学,咱们今天聊聊安全启动。说实话,这个模块是我在嵌入式安全领域里觉得最「硬核」的部分之一。你想想看,一个设备上电后,第一行代码是谁执行的?它凭什么可信?如果第一步就被篡改了,后面所有的安全措施都是白搭。

我当年做一款工业网关时,客户要求必须防住固件替换攻击。一开始我觉得,加个密码校验不就完了?结果被安全审计打回来三次。后来我才真正理解——安全启动不是加把锁,而是建立一条从硬件到软件的信任链。

4.1 安全启动的核心原理

安全启动,说白了就是「只运行经过签名的代码」。它的核心逻辑很简单:

  1. 上电后,第一段代码(BootROM)先运行。这段代码是出厂烧死的,改不了。
  2. BootROM 检查下一级 Bootloader 的签名。签名合法,才跳转执行。
  3. Bootloader 再检查应用程序的签名。以此类推,形成一条链。

为什么需要链式结构?因为 BootROM 空间有限,不可能把整个固件都塞进去。它只负责验证第一级,剩下的交给信任传递。

关键点:安全启动的强度取决于「信任根」的不可篡改性。如果 BootROM 能被改写,整个链条就断了。

4.2 信任根(RoT)的建立

信任根是安全启动的基石。它通常由硬件提供,比如 STM32 的 OTP(一次性可编程)区域HSM(硬件安全模块)

我个人习惯把信任根分为两类:

  • 不可变信任根:比如 BootROM,出厂后无法修改。STM32 的 System Memory 就属于这一类。
  • 可配置信任根:比如 OTP 中存储的公钥哈希。你可以烧录一次,之后只能读不能写。

我在项目中遇到过一个问题:客户想在生产后更新公钥。但 OTP 烧了就不能改。怎么办?后来我们用了「密钥轮换」方案——在 Bootloader 中预留多个公钥槽位,通过版本号选择。这样既保证了安全,又保留了灵活性。

我的建议:设计信任根时,一定要考虑「密钥失效」的场景。别把鸡蛋放在一个篮子里。至少预留一个备用公钥槽位。

4.3 签名验证流程

签名验证是安全启动的核心动作。流程大致如下:

  1. 计算固件哈希:使用 SHA-256 等算法,对固件内容取摘要。
  2. 用公钥解密签名:从固件头中取出签名值,用预置的公钥解密,得到原始哈希。
  3. 比对两个哈希:如果一致,说明固件未被篡改,且签名者持有私钥。

嗯,这里要注意:公钥本身也需要保护。如果攻击者替换了公钥,他就能用自己的私钥签名恶意固件。所以公钥必须存储在信任根中,比如 OTP 区域。

下面是一个简化的签名验证代码示例,基于 STM32H7 的 HASH 和 CRYP 外设:

// 伪代码:安全启动签名验证
int secure_boot_verify(uint8_t* firmware, uint32_t length) {
    uint8_t hash[32];
    uint8_t decrypted_sig[32];
    
    // 1. 计算固件哈希
    HAL_HASH_Start(&hhash, firmware, length, hash, 1000);
    
    // 2. 从固件头读取签名(假设偏移0x200处)
    uint8_t* signature = firmware + 0x200;
    
    // 3. 用公钥解密签名(RSA 2048)
    HAL_CRYP_RSADecrypt(&hcryp, signature, decrypted_sig, RSA_KEY_SIZE);
    
    // 4. 比对哈希
    if (memcmp(hash, decrypted_sig, 32) == 0) {
        return BOOT_SUCCESS;  // 签名验证通过
    } else {
        return BOOT_FAIL;     // 签名验证失败
    }
}

我曾经踩过的坑:一开始我把签名放在固件末尾,结果 BootROM 读取时越界了。后来统一规定:签名放在固定偏移处,且长度固定。这样 BootROM 不需要解析整个固件结构,既安全又高效。

4.4 安全启动的常见攻击与防御

你以为加了签名就万事大吉?太天真了。攻击者有的是办法绕过安全启动。我整理了几种常见攻击:

攻击类型 攻击方式 防御措施
回滚攻击 用旧版本固件替换新版本 固件头中加入版本号,BootROM 检查版本是否递增
电压毛刺攻击 在签名验证时注入电压毛刺,跳过检查 使用硬件安全模块(HSM)执行验证,抗物理攻击
JTAG/SWD 调试攻击 通过调试接口读取或篡改固件 量产时禁用调试接口,或设置密码保护
公钥替换攻击 篡改 OTP 中的公钥 OTP 区域写保护,且只能烧录一次

你想想看,如果攻击者能物理接触设备,他能做的事情太多了。所以安全启动不是万能的,它只是第一道防线。真正的安全需要多层防护。

4.5 实战建议:如何设计一个可靠的安全启动

最后,我结合自己的经验,给你几条实战建议:

  • 从硬件层面锁定信任根:使用 STM32 的 RDP(读保护)级别 2,彻底禁用调试接口。这样即使攻击者拿到芯片,也无法读取 BootROM 或 OTP 内容。
  • 签名算法选 RSA 2048 或 ECDSA:别用 RSA 1024,它已经不够安全了。我个人倾向 ECDSA,密钥更短,计算更快。
  • 加入防回滚机制:在固件头中存储版本号,并在 OTP 中记录「最大已运行版本」。每次启动时比较,拒绝旧版本。
  • 验证失败后不要直接死机:我见过很多设计,验证失败就 while(1)。这其实给了攻击者信息。更好的做法是:进入恢复模式,等待合法固件更新。

一个小技巧:在开发阶段,可以先用「测试公钥」调试安全启动流程。量产时再换成「生产公钥」。这样既不影响开发效率,又能保证最终产品的安全性。

好了,安全启动的内容就讲到这里。说白了,它就是一条信任链——从硬件到软件,每一环都要可信。下一章咱们聊聊「固件加密与解密」,看看如何防止固件被逆向分析。