ESP-IDF Secure Boot V1 完全指南:AES 硬件信任链的启用、密钥管理与底层算法解析
【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf
本篇围绕 ESP-IDF 中基于 AES 的 Secure Boot V1 方案展开,覆盖从 eFuse 信任链原理、menuconfig配置、一次性/可重烧写两种模式的完整烧录流程,到摘要(digest)算法与 ECDSA 镜像签名算法的源码级细节。读完本文,你将能够独立完成一次生产级的 Secure Boot V1 部署(含远程签名),并准确理解每一枚 eFuse 位与每一个idf.py secure-命令背后的硬件行为,同时掌握不启用硬件安全启动时"仅签名应用验证"的降级方案。
一、方案定位:Secure Boot V1 是什么,适用哪些芯片
Secure Boot 的核心目标是确保只有你编写的代码能在芯片上运行:每次复位时,从 flash 加载的数据都会经过校验。需要特别注意本文档(secure-boot-v1.rst)对应的是Secure Boot V1,即基于 AES 的安全启动方案,属于历史遗留(legacy)自定义方案;对于 ESP32 v3.0 及以后的版本以及 ESP32-S2,官方推荐改用 Secure Boot V2(RSA/ECDSA 方案)。
这一方案边界在源码的 Kconfig 中有直接体现。Kconfig.projbuild 中:
config SECURE_BOOT_V1_SUPPORTED bool default y depends on SOC_SECURE_BOOT_V1即 V1 是否可用完全取决于 SoC 能力位SOC_SECURE_BOOT_V1;同文件中SECURE_BOOT_V2_PREFERRED在ESP32_REV_MIN_FULL >= 300(ESP32 v3.0 及以上)时默认生效(见 Kconfig.projbuild),此时SECURE_BOOT_VERSIONchoice 会默认选中 V2(见 Kconfig.projbuild)。换言之,V1 实际上主要面向 ESP32(rev < 3.0),这与 Kconfig help 文本"Secure Boot V1 is the AES based (custom) secure boot scheme supported in ESP32 SoC"一致。
另外两条与 V1 强相关的基础事实:
- Secure Boot 与 Flash 加密 相互独立:可以只用安全启动不加密 flash,但从安全角度看两者应同时启用(原因见第七节);
- 启用 Secure Boot 是不可逆操作,会永久限制后续更新选项,操作前必须通读本文档;
- 理解安全启动流程的前提,是先了解标准 启动流程。
文中提到的大多数命令形如idf.py secure-<command>,它是对应espsecure <command>的封装,使用体验更友好但功能略少于原始工具。espsecure工具源码位于 esptool_py 组件目录。
二、背景原理:eFuse 中的密钥与 ABS_DONE_0 位
安全启动的"关键数据"并不存放在容易被物理读取的 flash 中,而是存放在芯片内部、软件不可访问的eFuse里,因此 flash 访问本身无需针对物理攻击做额外保护。eFuse 的分工如下:
- eFuse BLOCK2:存储 256 位的安全启动(bootloader)AES 密钥;
- ABS_DONE_0 位:写 1 后永久启用安全启动。
完整启动分为两级,安全启动在两个阶段都进行校验,形成信任链:
- 第一级:ROM 引导程序加载二级(软件)bootloader —— 由硬件安全启动电路校验;
- 第二级:bootloader 加载分区表与应用镜像 —— 由 ECDSA 签名校验。
三、安全启动全流程:首次启动与后续启动
官方文档给出的流程概要与每步的硬件行为如下:
- 配置入口:在项目配置菜单(
idf.py menuconfig)的Secure Boot Configuration中选择启用,并选择One-time Flash模式; - 默认行为:构建过程默认对镜像与分区表数据签名,
Secure boot private signing key配置项指向一个 PEM 格式的 ECDSA 公私钥对文件; - 构建 bootloader:ESP-IDF 构建出带安全启动支持的二级 bootloader,公钥被编译进 bootloader 镜像内部(因此公钥作为 bootloader 的一部分被隐式验证),该镜像烧录到 flash 偏移
0x1000; - 首次启动(关键步骤):二级 bootloader 执行以下动作:
- 硬件安全启动电路借助片上硬件 RNG 生成设备级 bootloader 密钥与摘要(digest)。密钥在 eFuse 中启用读写保护后存储;摘要由密钥、初始化向量(IV)和 bootloader 镜像内容共同导出;
- 摘要被写入 flash 偏移
0x0; - 根据 Secure Boot Configuration 的设置,烧录 eFuse 以永久禁用 JTAG 和 ROM BASIC 解释器(文档强烈建议开启这两项);
- bootloader 烧录
ABS_DONE_0eFuse 位永久启用安全启动。从此,只有摘要匹配的 bootloader 镜像才能引导。
- 后续启动:一级 ROM bootloader 发现安全启动 eFuse 已烧录,读取
0x0处保存的摘要,由硬件将其与现场新计算的摘要比对。摘要计算与比对完全在硬件内完成,计算出的摘要对软件不可读;不匹配则直接拒绝引导。 - 运行时验证:安全启动模式下,二级 bootloader 使用安全启动签名密钥(其公钥已嵌入 bootloader 内部,随 bootloader 一起被验证)对后续每一个分区表和应用镜像追加的签名做验证,通过后才允许引导。
二级 bootloader 的入口逻辑见 bootloader_start.c,首次启动时的密钥生成、摘要计算、eFuse 烧写都发生在其执行链路中。
四、两把密钥:AES bootloader 密钥与 ECDSA 签名密钥
Secure Boot V1 使用两把不同用途的密钥,混淆二者是常见错误来源:
1. 安全 bootloader 密钥(secure bootloader key)
- 256 位 AES 密钥,存放在 eFuse block 2;
- 默认由 bootloader 自身借助片上硬件 RNG 生成,用户无需提供(仅 Reflashable 模式例外,见第五节);
- 该 eFuse 在安全启动启用前即被设置读/写保护,防止软件后续读取密钥;
- eFuse Block 2 的编码方案(Coding Scheme)默认为
None,存储 256 位密钥。部分芯片版本上CODING_SCHEMEeFuse 值为 1(3/4 Encoding),此时该 block 必须存储192 位密钥——但算法始终以 256 位密钥运算,192 位密钥通过重复部分比特扩展为 256 位(扩展算法同 flash 加密,见 flash-encryption.rst)。
2. 安全启动签名密钥(secure boot signing key)
- PEM 格式的标准 ECDSA 公私钥对;
- 公钥被编译进二级 bootloader,仅用于验签,可自由分发、无需保密;
- 私钥必须严格保密——任何持有该私钥的人都能为任何配置了对应公钥的 bootloader 通过认证。
对应源码配置项(Kconfig.projbuild):
config SECURE_BOOT_SIGNING_KEY string "Secure boot private signing key" default "secure_boot_signing_key.pem" help Key file is an ECDSA private key (NIST256p curve) in PEM format for Secure Boot V1. Path is evaluated relative to the project directory. You can generate a new signing key by running: espsecure generate-signing-key secure_boot_signing_key.pem config SECURE_BOOT_VERIFICATION_KEY string "Secure boot public signature verification key" default "signature_verification_key.bin" help Key file is in raw binary format, and can be extracted from a PEM formatted private key using the espsecure extract-public-key command.注意 V1 的签名曲线固定为NIST256p(OpenSSL 称prime256v1),路径相对项目目录解析,且配置时文件不存在也不会报错——首次构建时才会检查并提示生成。
五、启用 Secure Boot(One-time Flash)的完整步骤
Secure Boot: One-Time Flash是生产设备的推荐配置:每台设备由硬件 RNG 生成一把独一无二的密钥,密钥永不离开设备。完整操作步骤:
打开项目配置菜单,在
Secure Boot Configuration下选择One-time Flash(对应 Kconfig.projbuild 中 choiceSECURE_BOOTLOADER_MODE的默认项SECURE_BOOTLOADER_ONE_TIME_FLASH);choice SECURE_BOOTLOADER_MODE bool "Secure bootloader mode" depends on SECURE_BOOT_V1_ENABLED default SECURE_BOOTLOADER_ONE_TIME_FLASH config SECURE_BOOTLOADER_ONE_TIME_FLASH bool "One-time flash" help On first boot, the bootloader will generate a key which is not readable externally or by software. ... Enabling this option means that the bootloader cannot be changed after the first time it is booted.为安全启动签名密钥指定文件名。相对路径基于项目目录解析;此项配置时文件无需存在;
设置其余 menuconfig 选项,特别留意
Bootloader Config——因为 bootloader 只能烧录一次。保存并退出;首次执行
idf.py build时,若找不到签名密钥,构建系统会打印错误并附带通过idf.py secure-generate-signing-key生成密钥的命令;执行
idf.py bootloader构建安全启动 bootloader,构建输出中会附带一条esptool write-flash烧录提示命令;手动执行该提示命令烧录 bootloader 并等待完成。这是一次性烧录——此后 bootloader 不可再更改;
执行
idf.py flash烧录分区表和刚构建的应用镜像(应用镜像在第 4 步生成的签名密钥下完成签名)。注意:启用安全启动后idf.py flash不会再烧录 bootloader;复位芯片。它将引导刚烧录的二级 bootloader,bootloader 在芯片上启用安全启动,随后验证应用镜像签名并启动应用。务必观察串口输出,确认安全启动已启用且无配置错误;
之后每次启动:安全启动硬件先用 secure bootloader 密钥校验二级 bootloader 未被篡改,再由二级 bootloader 用签名密钥公钥验证分区表和应用镜像的签名。
两个重要的时序保护机制:
- 安全启动不会在烧录有效的分区表和应用镜像之前就启用,目的是在系统完全配置好之前防止误操作;
- 首次启动期间复位或断电,下次上电会从头重新执行整个启用流程。
六、Reflashable 模式:可重烧写的二级 bootloader
One-Time Flash虽然最安全,但存在一种合法的替代模式——Reflashable(Kconfig.projbuild),它允许用户提供二进制密钥文件作为 secure bootloader 密钥,从而可以自己生成新的 bootloader 镜像与摘要并重烧:
Generate a reusable secure bootloader key, derived (via SHA-256) from the secure boot signing key. This allows the secure bootloader to be re-flashed by anyone with access to the secure boot signing key. This option is less secure than one-time flash, because a leak of the digest key from one device allows reflashing of any device that uses it.
在 ESP-IDF 构建流程中,这把 256 位密钥从用户的 ECDSA 应用签名私钥派生:取该私钥的SHA-256 摘要直接作为 eFuse 中的 secure bootloader 密钥(None编码方案下原样使用,3/4 Encoding方案下截断为 192 位)。这样设计的好处是用户只需生成和保护一把私钥。官方强烈建议不要在生产环境用同一把密钥烧录所有设备——One-Time Flash才是生产推荐。
启用步骤:
menuconfig 路径:
Bootloader Config>Enable hardware Secure Boot in bootloader>Enable Secure Boot version 1>Secure bootloader mode>Reflashable;如设备使用了
3/4 Encoding,需相应设置Hardware Key Encoding。编码方案可在esptool连接芯片时输出的Features行,或idf.py efuse-summary的输出中查看。对应配置项(Kconfig.projbuild):config SECURE_BOOTLOADER_KEY_ENCODING_256BIT bool "No encoding (256 bit key)" config SECURE_BOOTLOADER_KEY_ENCODING_192BIT bool "3/4 encoding (192 bit key)"按第六节的方法生成签名密钥,并把生成文件的路径填入
Secure Boot Configuration菜单;执行
idf.py bootloader:构建过程会创建一个由签名私钥派生的二进制密钥文件,并打印两组烧录步骤——- 第一组包含
idf.py efuse-burn-key secure_boot_v1 path_to/secure-bootloader-key-xxx.bin命令,用于把 bootloader 密钥写入 eFuse。这一步只能做一次; - 第二组用于在之后重烧 bootloader(使用构建期预先计算好的摘要);
- 第一组包含
回到第五节第 6 步继续:烧录 bootloader、启用安全启动,并密切观察串口日志确认无配置错误。
七、签名密钥的生成与远程签名
7.1 生成签名密钥
构建系统提示的命令idf.py secure-generate-signing-key底层使用 python-ecdsa 库,随机源是 Python 的os.urandom()(Linux/macOS 上即/dev/urandom,Windows 上为CryptGenRandom())。签名密钥的强度与系统随机源成正比——随机源弱则私钥弱。生产环境推荐用 OpenSSL 在具备高质量熵源的机器上生成:
openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key.pem安全启动体系的整体强度取决于签名密钥的保密性。
7.2 远程签名(Remote Signing)
生产构建的良好实践是让签名密钥只存在于独立的签名服务器上,而不是放在构建机上(后者是 ESP-IDF 的默认配置)。方法:
关闭
Sign binaries during build(对应 Kconfig.projbuild 的SECURE_BOOT_BUILD_SIGNED_BINARIES,默认y)。关闭后,构建机不需要私钥;但公钥仍然必须存在,因为它被编译进 bootloader,且 OTA 更新时需要验证镜像签名;从私钥提取公钥:
espsecure extract-public-key --keyfile PRIVATE_SIGNING_KEY PUBLIC_VERIFICATION_KEY并在 menuconfig 的
Secure boot public signature verification key中指定该公钥文件路径(默认名signature_verification_key.bin,裸二进制格式);应用镜像与分区表构建完成后,构建系统会打印需在签名服务器上执行的签名命令:
idf.py secure-sign-data --version 1 --keyfile PRIVATE_SIGNING_KEY BINARY_FILE该命令把签名追加到现有二进制尾部;用
--output可把签名结果写入独立文件:idf.py secure-sign-data --version 1 --keyfile PRIVATE_SIGNING_KEY --output SIGNED_BINARY_FILE BINARY_FILE
八、技术细节:硬件支持、摘要算法与签名算法
8.1 硬件安全启动电路的三种基本操作
第一级验证(校验二级 bootloader)完全由硬件完成,硬件电路提供三种操作:
- 从硬件 RNG 生成随机字节序列;
- 用 eFuse block 2 中的密钥对数据(通常是 flash 中的 bootloader 镜像)生成摘要。eFuse 中该密钥应当且能够被设置读写保护,阻止软件访问。仅当
ABS_DONE_0尚未烧录(仍为 0)时,摘要才能被软件读回; - 用与第 2 步相同的算法对数据生成摘要,并与缓冲区中提供的预计算摘要(通常读自 flash
0x0)比较,只返回 true/false,摘要本身不暴露给软件。该功能在ABS_DONE_0烧录后依然可用。
8.2 Secure Bootloader 摘要算法
算法以二进制"镜像"为输入,输出摘要(硬件文档中又称 "abstract")。Python 实现即espsecure工具(位于 esptool_py)的digest-secure-bootloader命令。带 (^) 标记的步骤是为满足硬件限制而非密码学限制:
- 以字节序反转方式从 eFuse block 2 读取 AES 密钥;若 Coding Scheme 为
3/4 Encoding,按 flash 加密所述算法把 192 位密钥扩展为 256 位; - 在镜像前缀接一个 128 字节的随机 IV;
- 若镜像长度不是 128 的倍数,用 0xFF 填充到 128 字节边界 (^);
- 对输入的每个 16 字节明文块:反转块内字节序 (^) → 施加AES256-ECB加密 → 反转密文字节序 (^) → 追加到总密文输出;
- 对密文的每个 4 字节字做字节交换 (^);
- 计算密文的SHA-512;
- 对摘要的每个 4 字节字做字节交换 (^)。
输出为 192 字节:128 字节 IV + 64 字节 SHA-512 摘要。
8.3 镜像签名算法
应用镜像与分区表采用符合 [RFC 6979] 的确定性 ECDSA(Deterministic ECDSA):
| 项目 | 取值 |
|---|---|
| 曲线 | NIST256p(OpenSSL 名prime256v1,亦称secp256r1) |
| 哈希函数 | SHA256 |
| 密钥存储格式 | PEM |
| 公钥在 bootloader 中的形式 | 64 字节裸二进制 |
| 镜像签名长度 | 68 字节 = 4 字节版本字(当前为 0)+ 64 字节签名数据,追加在应用镜像或分区表数据尾部 |
确定性 ECDSA(RFC 6979)的意义在于签名过程不依赖运行时的随机数,规避了因随机数缺陷导致私钥泄露的经典风险——这与第六节"密钥强度取决于熵源"的提示呼应。
8.4 独立手动命令
安全启动已集成进构建系统:idf.py build在启用安全启动时会自动签名应用镜像;idf.py bootloader会在 menuconfig 配置要求时生成 bootloader 摘要。但也可以离线生成独立签名与摘要:
签名二进制镜像:
idf.py secure-sign-data --version 1 --keyfile ./my_signing_key.pem --output ./image_signed.bin image-unsigned.binkeyfile为包含 ECDSA 私钥的 PEM 文件。
生成 bootloader 摘要:
idf.py secure-digest-secure-bootloader --keyfile ./securebootkey.bin --output ./bootloader-digest.bin bootloader/bootloader.binkeyfile为该设备的 32 字节裸 secure boot 密钥。输出文件同时包含摘要与 bootloader(摘要在前、bootloader 追加在后),烧录命令为:
esptool write-flash 0x0 bootloader-digest.bin九、Secure Boot 与 Flash 加密必须并用
单独使用 Secure Boot 而不启用 Flash 加密时,攻击者可以发起time-of-check to time-of-use(TOCTOU)攻击:在镜像被验证通过之后、真正运行之前,趁空隙替换 flash 内容。因此官方推荐两个特性同时启用,从"完整性验证"和"内容保密"两个维度共同构建安全环境。
另外,启用安全启动/flash 加密会增大 bootloader 体积,可能需要同步调整分区表中 app 分区偏移(bootloader size 相关说明见官方文档bootloader-size章节)。
十、降级方案:不启用硬件安全启动的签名应用验证
即使不启用硬件安全启动,也可以校验应用的完整性。该方案复用硬件安全启动的同一套应用签名机制,区别在于不阻止 bootloader 被物理更新——设备因此能防御远程网络攻击,但无法防御物理接触;实现上也比硬件安全启动简单得多。它提供两个独立开关:
- 更新时验证(Verification on update):启用后,凡是使用
esp_ota_ops.hAPI 进行 OTA 更新时都会自动检查签名。若硬件安全启动已启用则此项恒开且不可关;若未启用硬件安全启动,此项仍能显著防御网络攻击者伪造 OTA 更新,因此值得开启; - 启动时验证(Verification on boot):启用后 bootloader 会编译进"引导前验证应用签名"的代码。硬件安全启动启用时此项恒开;否则它单独提供的安全价值有限,多数用户应保持关闭。
启用步骤(对应配置项 Kconfig.projbuild 的SECURE_SIGNED_APPS_NO_SECURE_BOOT等):
- menuconfig >
Security features> 启用Require signed app images(CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT); - 可选启用
Bootloader verifies app signatures(CONFIG_SECURE_SIGNED_ON_BOOT_NO_SECURE_BOOT,默认关闭)实现启动时验证; - 选择
Require signed app images后,Sign binaries during build默认开启,构建过程会用Secure boot private signing key指定的私钥(默认secure_boot_signing_key.pem)自动签名二进制文件; - 若关闭
Sign binaries during build,则必须在Secure boot public signature verification key中指定公钥文件路径。此时私钥按第六节方法生成,公钥与签名镜像按 7.2 节的远程签名流程生成。
源码中这两项配置的帮助文本与文档逐字对应,例如SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT(默认y)的 help 说明"使用esp_ota_ops.hAPI 或esp_image_format.hAPI 时会验证签名",且"硬件安全启动启用时此项恒开"。
十一、最佳实践与 JTAG 影响
官方文档给出的 Secure Boot 最佳实践清单,建议逐条对照执行:
- 在拥有高质量熵源的系统中生成签名密钥;
- 签名密钥全程保密。一旦泄露,整个安全启动体系即告失守;
- 不允许任何第三方观察密钥生成或签名过程的任何环节(
espsecure或idf.py secure-子命令)。这两个过程都暴露于时序等侧信道攻击风险之下; - 开启 Secure Boot Configuration 下的全部安全选项,包括 flash 加密、禁用 JTAG、禁用 ROM BASIC 解释器、禁用 UART bootloader 对加密 flash 的访问;
- 将 Secure Boot 与 Flash 加密 组合使用,防止 flash 内容被本地读出。
关于调试:默认情况下启用安全启动时,bootloader 会在首次启动(与其同时启用安全启动的那一刻)通过 eFuse永久禁用 JTAG 调试。如果产品调试流程与安全启动有冲突,需在配置时权衡;官方文档在jtag-debugging-security-features章节进一步说明了安全启动或签名应用验证开启时如何兼顾 JTAG。
十二、关键要点速查
| 主题 | 要点 |
|---|---|
| 方案性质 | AES 自定义方案(V1),ESP32 SoC;ESP32 v3.0+/S2 请改用 V2 |
| 永久化载体 | eFuse BLOCK2 存 256 位 AES 密钥;ABS_DONE_0位永久启用;3/4 编码方案下密钥为 192 位 |
| flash 布局 | bootloader 摘要位于0x0(192 字节),二级 bootloader 位于0x1000 |
| 首次启动 | 硬件 RNG 生成密钥 → 写摘要到0x0→ 可选烧录禁 JTAG/BASIC → 烧ABS_DONE_0 |
| 生产模式 | One-Time Flash(每设备唯一密钥,推荐);Reflashable由签名私钥 SHA-256 派生密钥,仅测试/受控场景 |
| 签名算法 | 确定性 ECDSA(RFC 6979),NIST256p + SHA256,68 字节签名追加在镜像尾部 |
| 摘要算法 | AES256-ECB(逐 16 字节块,含字节序处理)+ SHA-512,输出 128B IV + 64B 摘要 |
| 关键命令 | idf.py secure-generate-signing-key/idf.py bootloader/idf.py secure-sign-data --version 1/idf.py secure-digest-secure-bootloader/espsecure extract-public-key |
| 常见风险 | TOCTOU(须同时开启 flash 加密)、一次性烧录不可逆、首次启动中断可重放、bootloader 体积增大需调整分区表偏移 |
原文档与源码交叉参考路径:Secure Boot V1 文档、Secure Boot V2 文档、Flash 加密文档、启动流程、bootloader Kconfig、二级 bootloader 入口、espsecure 工具组件。
【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考