STM32加密的核心就是给代码加上"三把锁":UID唯一ID锁 、Flash读保护(RDP)锁 和算法验证锁。我会为你详细说明从硬件、软件到具体操作的全过程。
📋 准备工作
-
🛠️ 硬件准备
-
STM32开发板 :入门级且支持加密的Nucleo系列就非常适合,比如 Nucleo-F103RB 或 Nucleo-G0/G4系列,集成的ST-Link调试器方便连接电脑。
-
USB数据线:用于连接开发板和电脑。
-
-
💻 软件准备
-
集成开发环境 (IDE) :STM32CubeIDE (ST官方免费、一体化)或 Keil MDK(业界标准,社区版免费)。个人推荐新手直接使用STM32CubeIDE。
-
烧录与配置工具 :STM32CubeProgrammer,这是ST官方用来烧录和配置芯片选项字节(如RDP)的必备工具。
-
串口调试助手 :用于查看程序输出,推荐使用免费的 VOFA+ 或 SSCOM。
-
💡 关键概念速览
在动手前,先快速理解三个核心概念:
-
UID (唯一ID):每颗STM32芯片出厂时内置的96位唯一ID,不可修改,是软件验证的基石。
-
RDP (读保护) :芯片的硬件"防盗门",通过选项字节 (Option Bytes) 配置。分为三个级别:
-
Level 0 (无保护):默认状态,调试口可随意读写Flash。
-
Level 1 (使能读保护) :量产标准配置。禁止调试口访问Flash,解除保护将触发全片擦除。
-
Level 2 (禁止调试):芯片调试接口永久关闭,不可逆,除非极端高安全需求,一般不用。
-
🛠️ 分步实操:给STM32加上三把锁
第一把锁:硬件读保护 (RDP) - 防止固件被直接读出
-
打开并连接
-
启动 STM32CubeProgrammer。
-
在右侧选择
ST-LINK并点击Connect。
-
-
进入选项字节配置
-
点击软件顶部的
Option Bytes标签页。 -
点击
Read按钮,软件会读出并显示当前芯片的配置。
-
-
设置读保护等级
- 在
Read Out Protection下拉菜单中,将默认的Level 0改为Level 1。
- 在
-
生效并验证
-
点击
Apply按钮,软件会弹出警告窗口,点击OK确认。 -
稍等片刻,软件会提示断开并重连。此时,任何试图读取或写入的操作都会失败,说明保护已生效。
-
第二把锁:软件唯一ID (UID) 校验 - 防止固件被复制到其他芯片
-
生成密钥工程
-
打开你的IDE (STM32CubeIDE) 新建一个工程。
-
在
main.c中编写一个一次性运行的函数,专门用于"签发身份证":c
// 伪代码示例,需要在芯片第一次运行时执行,并将结果写入Flash。 void Generate_Key_Once(void) { uint32_t u_id[3]; // 用于存储UID的三个32位字 // 假设UID从地址0x1FFF7A10开始,不同系列地址可能不同,请查阅手册 u_id[0] = *(uint32_t *)(0x1FFF7A10); u_id[1] = *(uint32_t *)(0x1FFF7A14); u_id[2] = *(uint32_t *)(0x1FFF7A18); // 使用UID和一些自定义的“盐”值来生成密钥 uint8_t key[16]; Generate_Key_From_UID(u_id, "Your_Secret_Salt", key); // 将生成的密钥写入Flash的最后几页,供以后校验 Write_Key_To_Flash(key); // 写入一个标志,表示“已经签发过身份证了”,避免重复执行 Write_Flag_To_Flash(0x5A5A5A5A); } -
将这个程序烧录到你的开发板上运行一次,使其将生成的密钥存入Flash。
-
-
校验密钥工程
-
修改
main.c,在程序最开始添加校验函数:c
// 伪代码示例,必须在所有用户功能之前执行 int Check_Key(void) { // 1. 检查是否已生成密钥的标志 if (Read_Flag_From_Flash() != 0x5A5A5A5A) { // 若标志不存在,说明是未初始化的芯片,可拒绝运行或进入恢复模式 return 0; } // 2. 重新读取UID uint32_t u_id[3]; Read_UID(u_id); // 3. 使用完全相同的算法,重新计算预期的密钥 uint8_t expected_key[16]; Generate_Key_From_UID(u_id, "Your_Secret_Salt", expected_key); // 4. 读取Flash中之前存储的密钥 uint8_t stored_key[16]; Read_Key_From_Flash(stored_key); // 5. 比较两者是否一致 if (memcmp(expected_key, stored_key, 16) != 0) { // 密钥不匹配!说明固件被复制到了别的芯片,启动“自毁”或“迷惑”程序 while(1); // 进入死循环 } return 1; // 校验通过 } -
点击
Build按钮编译工程。
-
第三把锁:最终烧录与启用
这是量产前的最后一步,目的是将校验逻辑和读保护结合,让固件坚不可摧。
-
烧录含UID校验的固件 :使用STM32CubeProgrammer将上一步编译好的固件烧录进开发板。
-
启用第一把锁(RDP Level 1) :重复"第一把锁"的步骤,在
Option Bytes中再次将读保护设置为Level 1并Apply。 -
最终验证:断开并重连STM32CubeProgrammer,如果一切顺利,它会报告无法访问,代表加密成功。
🚀 进阶保护:为固件穿上"防弹衣"
完成以上三步,你的固件安全级别已经很高了。如果想更进一步,可以考虑:
-
启用写保护 (WRP):和设置RDP在同一界面,勾选需要保护的Flash扇区即可。
-
代码混淆:让攻击者难以逆向分析。
-
添加"反调试"代码:检测调试寄存器,一旦发现即让程序停止或自毁。
📌 几点提醒
-
RDP Level 1是量产首选:它提供了足够的安全性,同时允许通过全片擦除的方式来"解锁"返修品,适合量产和维修流程。
-
RDP Level 2极难恢复:一旦设置,芯片基本报废。在量产初期请勿使用,在最终产品定型且确定不再需要调试时,才有必要考虑。
-
密钥"盐值"是最高机密 :代码中生成密钥时使用的"盐值"
Your_Secret_Salt是核心机密,务必保管好。 -
务必先测试后量产:在大批量生产前,一定要用几块板子完整地走一遍上述流程,确保万无一失。
-
善用ST官方工具:新系列STM32H5、U5等支持更高级的安全固件安装(SFI),可直接烧录加密后的固件,实现端到端的安全。
加密是一个系统工程,没有绝对的安全,只有不断提高的破解成本。