STM32C5上基于OEMiROT的安全启动配置完整指南

发布时间:2026/8/30 9:12:39
STM32C5上基于OEMiROT的安全启动配置完整指南 从去年开始我陆陆续续在评估新一代 MCU 做产品迭代ST 的 STM32C5 系列一出来我就盯上了。Cortex-M33 内核、主频能干到 250MHz、Flash 最大 2MB加上 TrustZone、HASH、CRYP 这些安全外设非常适合做带安全启动的联网设备。但真正上手之后才发现TrustZone 本身就够折腾了再加上 OEMiROT 这套安全启动方案配置链路过长官方文档又分散第一次跑通花了我不少时间。所以这篇文章我打算把在 STM32C5 上用 STM32CubeIDE STM32CubeMX 配置 OEMiROT 的完整过程梳理出来从原理讲到实操再到调试期踩过的坑。不管你是第一次接触安全启动还是已经从 STM32H5 迁移过来这篇内容应该都能帮你省掉不少试错时间。1. OEMiROT 到底是什么为什么选在 STM32C5 上做很多刚接触这块的开发者会把 OEMiROT 和普通的“固件加密”混在一起实际上两者解决的问题完全不同。固件加密防的是别人把 Flash 里的二进制 dump 出来分析而 OEMiROT 解决的是“启动时如何确保跑起来的是合法固件”。我当初也是被这个概念绕了一下所以先把这个讲清楚。1.1 信任根和安全启动的基本概念安全启动链路里最核心的词叫 RoTRoot of Trust信任根。你可以把整条启动链想象成一条“担保链”上电后第一段代码必须是被信任的它去检查下一段代码的签名和完整性通过了才把控制权交过去下一段代码再检查更下一段。最开头那个“谁来信任第一段代码”的锚点就是信任根。在 STM32C5 上这个信任根固化在芯片内部 ROM 里上电后 CPU 会先执行 ROM 里的代码由它来决定是进入 boot 流程还是其他模式。OEMiROT 的全称是 OEM Read Only Trust意思是“由 OEM也就是你自己提供密钥的只读信任根”。它的思路是ROM 里放一段固定的引导代码默认信任的第一级用户固件是存放在 Flash 里的 OEMiROT_Boot而校验这把密钥由你来定。也就是说如果别人想替换你的 boot 固件但没有你的私钥签出合法镜像芯片就不会执行。1.2 OEMiROT 和 STiROT 的分工在 ST 的产品线里安全启动通常还涉及另一个概念叫 STiROT即 ST 提供的信任根。两者最大区别在于密钥掌控权。STiROT校验密钥由 ST 生成OEM 拿不到私钥适合那些完全信任 ST 生态、不想自己管密钥的场景但因为密钥不在自己手里做产品定制和售后换固件时会比较受限制。OEMiROT校验密钥由 OEM 自己生成和管理签发、吊销、升级都由自己控制适合真正做产品量产的公司。我建议在产品开发阶段就果断选 OEMiROT因为密钥在自己手里后面调整生产策略、维护测试固件都方便。而且 STM32C5 的 ROM 里同时支持 STiROT 和 OEMiROT两个可以配合使用但大多数场景用 OEMiROT 就够了。1.3 STM32C5 硬件上为 OEMiROT 准备了什么STM32C5 做 OEMiROT 有几个很关键的硬件基础缺一个链路就跑不通TrustZone把 Flash、SRAM 和外设分成安全/非安全两个世界安全启动链里的 Secure 固件运行在安全区NonSecure 固件运行在非安全区。HASH 和 CRYP 外设用于固件摘要计算和解签校验比纯软件实现快很多。OTPOne-Time Programmable区域用来存放 OEMiROT 的公钥哈希、密钥配置等烧进去后不可篡改这是信任根能够成立的关键。RDPRead Protection读保护等级管理用于保护 Flash 内容不被调试口随意读取。OEMiROT 启用后一般要求 RDP 至少到 Level 1量产时甚至可以到 Level 2。当初我选 STM32C5 也是看中它把这几块都集成全了不用像以前那样外挂一颗安全芯片BOM 成本省下来不少。2. 跑通 OEMiROT 前最好先想清楚的几件事很多人拿到板子就开干结果卡在版本不匹配、链接脚本地址重叠、烧录顺序错误这些问题上。其实这些坑大部分可以在动手前通过“先规划再实施”来避免。2.1 开发板、工具和固件包的版本匹配STM32C5 是较新的系列对工具链版本有要求太老的 STM32CubeMX 或 STM32CubeIDE 根本生成不了对应的工程甚至装不上设备支持包。我目前用的组合是组件版本要求说明开发板NUCLEO-C5 系列自带 ST-LINK/V3调试烧录方便STM32CubeMX6.13 及以上新版本才内置 OEMiROT 配置向导STM32CubeIDE1.16 及以上旧版本对 TrustZone 工程支持不全STM32CubeC5 固件包1.0.0 及以上包含 OEMiROT 相关驱动和示例X-CUBE-SBSFU最新版本安全启动工具链辅助密钥生成和烧录下载 CubeMX 固件包时经常遇到网络慢或登录问题我的经验是直接去 ST 官网下载离线安装包比在 CubeMX 里在线拉取稳定得多。同时用 STM32CubeProgrammer 来承担密钥烧录、RDP 切换等工作版本也建议保持最新。2.2 密钥策略示例密钥还是自有密钥OEMiROT 的核心是密钥。ST 在 CubeMX 生成工程时默认会附带一套示例密钥专门用来让你先把链路跑通。这套密钥用于开发和功能验证没问题但绝不能用于量产。我的建议是第一次做的时候直接用示例密钥跑通整个启动链之后再换成自己的密钥。原因是密钥一旦写进 OTP修改会很麻烦OTP 不可擦除开发阶段没必要给自己设障碍。等链路通了再按自己的密钥方案重新生成一遍即可。2.3 分区规划Flash 里三个工程的安放OEMiROT 在 STM32C5 上典型布局是把 Flash 分成三块区域OEMiROT_Boot最前面的 boot 代码负责校验 Secure 固件。OEMiROT_Secure安全世界固件运行在 TrustZone 的安全区负责校验 NonSecure 固件。OEMiROT_NonSecure非安全世界固件也就是你的实际业务代码。在 CubeMX 生成工程时会自动算出地址和大小但你必须心里有数否则后面改了 Flash 大小或者加了功能很容易让链接脚本地址重叠。以 2MB Flash 版本为例常见划分是 Boot 占 64KB、Secure 占 256KB、NonSecure 占剩余空间。这个分配不是死的取决于你的 boot 功能复杂度和你自己的安全策略。刚开始我并不建议手动去改这些地址先用 CubeMX 默认的分配方案跑通再根据实际需求微调。3. 在 STM32CubeMX 里一步步配出 OEMiROT 工程这部分是整个过程的重点。我按实际操作顺序写你在自己机器上照着点就行。配置过程中有一个大原则能点默认就用默认不要贪多。OEMiROT 牵扯到安全分区、RDP、OTP 多个模块手动改动越多出问题的概率越大。3.1 新建工程和开启 TrustZone打开 STM32CubeMX选择你的具体型号比如 STM32C5x 系列。在 Project Manager 页面里有几个关键设置需要提前确认工程名称和路径不要带中文和空格。Toolchain 选择 STM32CubeIDE。勾选 Generate Under Root 选项这样生成出来的工程结构是 Boot、Secure、NonSecure 三个子工程放在同一父目录下方便后续统一编译。进入 Pinout Configuration 页面后先找到 TrustZone 相关的总开关。这个通常在系统内核配置里点击后 CubeMX 会提示你进行安全/非安全分区设置。TrustZone 的启用是整个 OEMiROT 工程的前提不开启这个后面根本没有 OEMiROT 选项。开启 TrustZone 后系统会自动把 Flash 和 SRAM 划分成安全和非安全两个区域。你可以看到安全世界和非安全世界各自的起始地址和大小。这里 STM32C5 的 Flash 起始地址是 0x0C000000和以往系列不同注意别搞混。3.2 选择 OEMiROT Boot 路径TrustZone 开启后在 Security 相关的配置选项里会多出 Boot 路径选择。这个选项在不同 CubeMX 版本里位置略有差异但大体逻辑一致要么选 STiROT、要么选 OEMiROT、要么选禁用。我直接选了 OEMiROT。选完后 CubeMX 会自动加载 OEMiROT 的配置界面里面会有几个关键参数固件签名算法支持 RSA 和 ECDSA默认一般是 ECDSA P-256。选默认即可。公钥哈希存放位置默认放在 OTP。这里有两个选择一个是让 CubeMX 先生成示例密钥对另一个是导入已有公钥。第一次先选示例密钥。防回滚版本号用于固件降级保护默认从 0 开始后面每次发布新固件递增。这个界面是整个配置的核心。千万不要为了“看起来更安全”去改一些看不懂的参数比如哈希算法位宽、签名算法曲线等改错了后面编译能过但启动校验必挂。3.3 配置 RDP、OTP 和调试认证在 OEMiROT 配置页里还有几个安全和调试相关的选项需要处理RDP 等级开发阶段选 Level 0方便随时烧录和调试量产出货时改为 Level 1。Debug Authentication调试认证如果你要把 RDP 切到 Level 1就得配置调试认证方式。这块如果不做设置后面 RDP Level 1 时调试器连不上芯片固件就没法升级了。我在开发阶段保持 RDP Level 0所以这个可以先跳过。OTP 配置生成工程时CubeMX 会把 OEMiROT 需要的公钥哈希等数据生成到一个独立的二进制文件后续用 STM32CubeProgrammer 烧进 OTP。这里特别提醒一下RDP 等级一旦切到 Level 1Flash 的读保护就开启了普通方式无法直接读取固件内容。从 Level 1 降回 Level 0 会触发全片擦除所以不要在产品有重要数据时随便切换 RDP 等级。3.4 生成三个子工程所有配置完成后点击生成代码。CubeMX 会生成三个工程目录OEMiROT_BootOEMiROT_SecureOEMiROT_NonSecure这三个工程是独立的 STM32CubeIDE 工程但生成时要注意CubeMX 的 Generate Under Root 选项如果没勾三个工程会被拆开后面编译顺序比较容易乱。生成完可以打开工程看一眼每个工程的 Debug 配置里应该已经自动配好了对应的 Flash 下载算法和起始地址。这一点是 CubeMX 做得比较省心的地方。4. 编译、烧录、首次启动的完整链路三个工程生成好了接下来就是编译和烧录。这个环节看着简单但顺序错误会造成很迷惑的启动失败现象所以我要单独写一节。4.1 编译顺序为什么不能乱OEMiROT 三个工程之间存在依赖关系Secure 工程编译时需要 Boot 工程产出的签名工具和配置NonSecure 工程编译时需要 Secure 工程产出的固件来生成签名镜像。因此正确编译顺序是先编译 OEMiROT_Boot。再编译 OEMiROT_Secure。最后编译 OEMiROT_NonSecure。第一次我图省事直接从 NonSecure 开始编译结果提示找不到 Secure 固件折腾了半天。后面老实按顺序来一次就过了。在 STM32CubeIDE 里你可以手动按这个顺序逐个编译也可以把三个工程导入同一个 Workspace 后自行调整构建顺序。我自己更喜欢手动逐个编译因为每一步都能看到输出排查问题更直观。编译时如果报错提示缺少某些头文件或者链接脚本路径不对绝大多数情况是 CubeMX 生成工程时路径里有中文或空格导致的把工程迁到纯英文路径下重新导入即可。4.2 擦除和烧录的正确姿势烧录前先把芯片恢复到干净状态。用 STM32CubeProgrammer 连上 ST-LINK执行全片擦除然后按以下顺序烧录顺序烧录内容目标地址说明1OEMiROT_Boot 固件Boot 起始地址由 STM32CubeProgrammer 自动识别2OEMiROT 密钥/OTP 数据OTP 区域只烧一次不可擦除3OEMiROT_Secure 固件Secure 起始地址需通过 boot 校验4OEMiROT_NonSecure 固件NonSecure 起始地址需通过 secure 校验注意这里 OTP 数据的烧录只做一次。如果反复烧录不同密钥第一次的 OTP 内容已经固化后面操作会冲突。所以量产时最好是主板贴片前先烧好 OTP或者用独立的烧录工装统一处理。我自己实际用 STM32CubeProgrammer 烧录时Boot、Secure、NonSecure 三个固件是可以一次性批量添加的只要地址配置正确它会按顺序写入。唯一要小心的是 OTP 区域写入选项别手滑把 OTP 当成普通 Flash 反复擦写。4.3 首次上电后应该看到的现象烧录完理论上按一下复位系统应该依次执行 Boot - Secure - NonSecure。怎么确认每步都正常最简单的方式是借助串口。在 CubeMX 生成工程时通常默认会开启一个调试串口Boot 阶段会打印启动日志Secure 阶段会打印校验结果NonSecure 阶段会打印应用启动信息。我用的板子串口映射到了 ST-LINK 的虚拟串口波特率 115200打开串口助手就能看到。如果串口输出类似 “OEMiROT Boot: Image verified OK” 的日志说明 Boot 对 Secure 的校验通过了再看到 Secure 打印出对 NonSecure 的校验结果整个链路就通了。如果卡在某一步一般有对应的错误提示比如 “Image not verified” 或 “Auth failed”这时候按错误提示反查即可。4.4 从开发模式切换到发布模式开发阶段 RDP 是 Level 0密钥是示例密钥链路跑通后如果想验证“产品真实环境”需要做几件事用自有密钥重新生成 Boot、Secure、NonSecure 工程烧录时使用新密钥的 OTP 数据。把 RDP 从 Level 0 切换到 Level 1。把防回滚版本号设为合理初始值确保后续版本只能递增。这步一旦执行芯片就进入“生产态”。之后如果你想再改固件必须用正确密钥签名否则芯片不认。这也是很多人在开发板上前期调试很顺利一到“发布模式”就翻车的原因——烧进去的固件没签新密钥直接被拒了。5. 验证方案、调试技巧和踩坑实录链路跑通只是开始真正烦人的是后面调试阶段。我把这半年遇到的高频问题和排查思路整理出来希望能帮你绕开。5.1 怎么验证 OEMiROT 真的在保护固件很多人跑通启动链后觉得万事大吉其实应该做一次“破坏性测试”来验证安全启动是否真的生效。用 STM32CubeProgrammer 读取 Flash 里的 Secure 固件正常情况会因为 RDP 保护读不出来或者只能读到密文。把 NonSecure 固件区域篡改几个字节然后复位观察启动是否失败。用非法的私钥重新签名一份 NonSecure 固件烧进去后看是否被拒绝启动。这三项测试如果都按预期工作OEMiROT 才算真正生效。我自己做测试时胆子比较大直接拿错误的密钥签了一个固件烧进去结果芯片反复复位串口持续打印校验失败日志完全符合预期。这里有个细节提醒破坏性测试之后一定要重新烧录正确固件再进入后续开发否则可能会把板子卡在一个反复复位的状态干扰排查。5.2 调试时最容易踩的几个坑我总结下来OEMiROT 调试期踩坑基本集中在以下几类坑 1RDP Level 切换后连不上调试器。这是最常见也最坑的一个。RDP 切到 Level 1 后调试器的连接会受调试认证策略影响如果没配好 Debug AuthenticationST-LINK 会报连接错误。我的解决方案是开发阶段尽量不切 RDP Level 1等到最后发布前再切。如果已经切了且进不去需要先用 STM32CubeProgrammer 做 Mass Erase把 RDP 降回 Level 0会擦除所有 Flash 内容。坑 2OTP 烧录后密钥不匹配。有时候明明按步骤烧了 OTP但启动时还是报签名校验失败。这时候先检查 OTP 里烧的密钥是否和编译 Boot/Secure 时用的密钥一致。我把示例密钥换成自有密钥时曾因为 CubeMX 工程里没有同步更新密钥配置导致 Boot 用新公钥验签而 OTP 里还是旧公钥启动一直失败。坑 3Flash 地址重叠。修改了 Secure 固件大小后NonSecure 的 Flash 起始地址没有联动更新编译能过烧录也能过但 NonSecure 固件被 Secure 固件覆盖启动后行为完全不可控。排查方式是打开每个工程的链接脚本确认 Flash 区域地址没有重叠。坑 4时钟配置影响签名校验时间。这个很多人忽略。OEMiROT 签名校验涉及 HASH 计算如果时钟没配好HASH 外设工作异常启动会卡在校验阶段。我遇到过一次 Boot 卡死最后发现是时钟树配置里 HASH 的时钟源没使能重新配置后恢复正常。5.3 恢复和故障排除流程调试过程中板子被搞到无法启动、连不上调试器是常事。我的处理流程是按住板子复位键用 STM32CubeProgrammer 连接如果能在复位瞬间连上立即执行全片擦除。如果完全连不上短接 BOOT 引脚进入系统 bootloader 模式用串口或 USB 方式连接后再擦除。擦除成功后重新按顺序烧录 Boot、OTP、Secure、NonSecure。这套流程基本能覆盖 90% 的“变砖”场景。真正变砖通常是因为 OTP 区被写坏但正常操作下 OTP 写入是可控的不用太担心。这里还有一个小经验每次改动工程配置后我都习惯性先编译 Boot 工程再编译 Secure 工程最后编译 NonSecure 工程并且编译完成后对比一下生成的固件大小看有没有异常膨胀。如果某个固件大小突然超过分配的空间八成是链接脚本或配置出了问题早发现早解决。6. 从开发到量产OEMiROT 还需要补哪些功课开发板上跑通 OEMiROT 之后如果你准备往量产走有几个点现在就可以开始考虑。首先是密钥管理流程。开发阶段用示例密钥无所谓但正式产品一定要有完整的密钥生成、备份、销毁流程。私钥建议放在加密机或专用的密钥管理系统中不要直接散落在开发机和工作电脑里。否则一旦私钥泄露整个产品线的安全启动形同虚设。其次是生产烧录流程。量产时不可能每块板子都用 STM32CubeProgrammer 手动烧一般会通过产测工装把固件和 OTP 数据一起灌进去。这时候需要确保产测工具支持烧录 OTP且烧录顺序和你验证时一致。最后是固件升级策略。OEMiROT 的防回滚机制会影响后期 OTA 升级版本号规划要从一开始就定好规则避免出现版本号错乱导致设备无法升级的情况。我自己在项目初期就定了一条规矩主版本号不变次版本每次发布必须单调递增紧急修复也不允许降低版本号。我实际测试过用 STM32CubeProgrammer 配合脚本做产线烧录效率和稳定性都不错。如果你有自己的产测上位机也可以直接调用 STM32CubeProgrammer 的命令行接口把烧录流程脚本化集成到产测流程里。最后分享一个我自己一直在用的实操习惯初次跑 OEMiROT 时无论多急也一定先按“示例密钥 默认分区 RDP Level 0”把整个链路完整跑通一次再考虑换成自己的密钥或调整分区。这个看起来“绕远路”的做法其实是最省时间的。我见过太多同事一上来就直接用自签密钥结果链路没跑通密钥还烧进了 OTP后面排查又麻烦又受限。先把默认流程走通你就掌握了所有环节的正确预期再一步步定制化才能做到心中有数。