STM32加密

张开发
2026/4/21 2:07:48 15 分钟阅读

分享文章

STM32加密
1.概述学习stm32加密技术要先知道stm32系列的单片机上一个简单的单片机程序是怎样的C要知道为什么需要加密知道有哪些加密技术接着我们要先知道别人一般是怎么盗取我们的程序才知道在哪些途径或地方要去加密2.网课教程加密教学1.生成密钥 (出厂时运行一次)读取UID应用MD5算法得到一个预存值再把它作为种子去生成随机数并写入Flash。2.校验密钥 (每次启动时运行)重复计算过程与Flash中的预存值比对不一致则擦除程序或进入死循环或安全模式。3.开启RDP读写保护这个教学防范的是生成密钥需要防破解校验密钥是为了防止攻击者把你的固件原封不动地复制到另一颗芯片上运行开启RDP读写保护防止固件被读取。即首次生成flash解锁读取uid对uid加盐写入flashflash上锁每次运行检查读取指定位置uid按秘钥公式计算结果对比设置RDP保护 成功/失败3.对课程的补充你的总结非常准确抓住了课程的核心三步生成密钥 → 校验密钥 → 开启 RDP 读保护。而且你对每一步的防范目标也理解到位防止破解、防止固件被复制到别的芯片、防止固件被直接读取。在此基础上我再补充几个关键细节和常见遗漏让你的总结更完整、更实用。✅ 你的总结已经正确的部分步骤操作防范目标1. 生成密钥出厂一次读取 UID → MD5 得到固定值 → 作为种子生成随机数 → 写入 Flash让每颗芯片拥有唯一密钥防止批量克隆2. 校验密钥每次启动重新计算 UID MD5与 Flash 中的预存值比对不一致则死循环防止固件被直接复制到另一颗 UID 不同的芯片上运行3. 开启 RDP 读保护设置选项字节为 RDP Level 1 或 Level 2防止通过调试器ST-Link/J-Link读出 Flash 中的固件和密钥 需要补充完整的几个技术细节1. 为什么生成密钥时要用“随机数”你提到“把 MD5 结果作为种子去生成随机数并写入 Flash”这看似多了一步其实有实际意义直接存储 MD5(UID)是可以的但攻击者如果知道你的算法可以自己计算出来但无济于事因为 UID 不同。生成随机数再存储通常是为了后续使用该随机数作为对称加密的密钥比如 AES 加密通信数据。此时 MD5 只是用来验证 UID 是否正确真正的加密密钥是随机生成的增加破解难度。简单场景下完全可以省略随机数直接存储MD5(UID 自定义常数)作为比对值。2. RDP 的三个级别及注意事项Level 0无保护调试接口全开。Level 1读保护开启调试接口无法读取 Flash要解除保护会触发mass erase全片擦除固件和密钥都被清空。这是量产产品最常用的级别。Level 2调试接口永久关闭且无法降级到 Level 1 或 0。芯片不再接受任何调试连接。一旦设置芯片几乎无法再被读取或改写除非使用特殊的后门一般不存在。适合极高安全要求的设备。⚠️重要开启 RDP Level 1 或 2 后你不能再通过调试器烧录新固件除非先解除保护并擦除芯片。所以必须在烧录完整合了校验代码的固件后最后一步再通过代码设置 RDP 位或者使用烧录工具在烧录完成后自动设置。3. 校验失败的处理不要只死循环简单的while(1);很容易被攻击者通过复位或电源干扰绕过。更可靠的做法进入硬件陷阱例如随机延时后调用NVIC_SystemReset()不断复位或者故意触发硬件错误除零、非法地址访问。功能降级不直接死机而是禁用所有关键功能比如输出错误数据、停止电机驱动让设备看起来正常但实际不可用。多次校验在程序的不同位置、不同时机反复校验例如在中断服务函数、定时器回调里也加校验。4. 防止校验代码本身被跳过或修改即使有 RDP攻击者如果通过故障注入如电压毛刺、激光照射让 CPU 跳过校验指令依然可能运行你的程序。因此将校验代码放在PCROP专有代码读保护区域该区域的代码只能执行不能通过调试器或 CPU 数据读访问读取。这样攻击者无法分析你的校验逻辑。使用校验和对整个 Flash 或关键代码段计算 CRC与预先存储的值比对防止代码被篡改。5.校验这一步是为了防止攻击者把你的固件原封不动地复制到另一颗芯片上运行。下面我用一个场景来解释它的作用以及它为什么能起到防范作用。 校验的核心目的实现“一机一码”绑定你的方案本质上是让每个芯片的固件和它唯一的UID绑定。出厂时你的编程器操作读取芯片A的UID → 经过MD5随机数种子 → 生成一个独一无二的密钥Key_A。把Key_A写入芯片A的Flash中。运行时芯片A自己执行再次读取自己的UID → 用完全相同的算法计算出Key_A‘。将Key_A’与Flash中预存的Key_A进行比较。如果相等说明这颗芯片确实是“原装”的程序继续运行。如果不相等进入死循环或安全模式程序不工作。️ 它能防范什么它能有效防止“固件克隆”攻击。假设攻击者通过某种手段比如从你的工厂废品中读出了芯片A的完整Flash内容包含了你的程序代码 预存的Key_A。然后他买了一批空白的芯片B、C、D……想把读出的固件直接烧录进去。结果会怎样芯片B启动后会读取自己的UID_B计算出Key_B’。但它Flash里存的是Key_A从芯片A复制过来的。Key_B‘ ≠ Key_A校验失败 → 芯片B拒绝运行。攻击者得到的固件只能在芯片A上运行换任何其他芯片都无效。这就等于让你的程序“锁死”在原始芯片上无法批量克隆。 那它防不住什么你的校验方案也有明显的弱点主要是下面两种攻击暴力跳过校验攻击者拿到你的固件二进制后不修改密钥而是直接用反汇编工具找到校验失败的跳转指令比如if (wrong) goto deadloop;把它改成goto normal_run;然后重新烧录。这样校验就被“阉割”了。如何应对不要把校验做成一个明显的if分支。可以把校验结果作为后续解密的密钥或者让校验失败后随机死机、破坏关键数据增加定位难度。算法逆向攻击者如果反编译了你的固件看到你是用MD5(UID) - srand() - rand()他完全可以自己写个小程序输入任意UID就能算出对应的Key。这样他就能批量生成合法密钥然后写入克隆芯片。如何应对不要使用标准库的srand/rand它的序列是公开的。改用你自己设计的、不可逆的混淆算法或者直接使用硬件加密引擎如STM32的AES结合UID生成密钥让攻击者即使看到代码也无法轻易逆向出从UID到Key的映射关系。✅ 总结校验这一步到底有没有用有用而且非常必要。它是实现“一机一码”防复制的核心逻辑。没有这个校验你的密钥生成得再复杂也是白费——因为程序根本不会去检查密钥是否正确。但是仅仅有这个校验还不够。你必须配合读保护RDP Level 1或2防止攻击者轻易读出你的固件二进制从而无法分析你的校验算法。代码混淆/防篡改让攻击者难以通过简单修改二进制来跳过校验。强密钥派生算法让从UID到Key的计算不可逆、不可预测。你的方向是正确的只要在这个基础上加固算法和物理防护就能有效提升盗取成本。 更完整的加密流程课程可能未深入讲但建议了解text┌─────────────────────────────────────────────────────┐ │ 1. 芯片出厂/首次烧录 │ │ - 烧录 Bootloader 应用程序不含校验数据 │ │ - 运行一次密钥生成函数 │ │ key MD5(UID 自定义常数) │ │ 可选生成随机数 enc_key │ │ 将 key 和 enc_key 写入 Flash 的特定扇区 │ │ - 开启 RDP Level 1 │ ├─────────────────────────────────────────────────────┤ │ 2. 每次上电运行 │ │ - 读取 UID重新计算 key │ │ - 与 Flash 中的 key 比对 │ │ - 不一致 → 进入死循环/复位/功能降级 │ │ - 一致 → 正常启动可选使用 enc_key 进行通信加密 │ │ - 运行时随机位置再次校验防止被跳过 │ └─────────────────────────────────────────────────────┘ 你的总结最终版建议尚硅谷 STM32 加密课程核心思想利用芯片唯一 IDUID结合 MD5 哈希算法为每颗芯片生成独一无二的密钥并在每次启动时校验该密钥。若密钥不匹配说明固件被复制到其他芯片则拒绝运行。同时开启 RDP 读保护防止攻击者通过调试接口直接读取 Flash 中的固件和密钥。此外还可以配合 PCROP、校验和、运行时多点校验等方法增强安全性。 扩展知识攻击者可能的盗取途径帮助你理解为何要这样加密攻击方式原理对应防御措施调试接口读取使用 ST-Link 直接连接 SWD/JTAG 读出 FlashRDP 读保护固件复制到同型号空片把读出的固件烧录到另一颗 STM32 上UID 校验新芯片 UID 不同校验失败反汇编 跳过校验用 IDA Pro 等工具分析固件找到校验函数并 patch 掉PCROP隐藏校验代码、校验和防止修改故障注入在 CPU 执行校验指令时用电压毛刺使其跳过多次校验、随机延时校验、硬件安全单元物理拆片读取去除芯片封装用探针直接读取 Flash 单元RDP Level 2 顶层金属屏蔽仅部分高端 MCU 支持你的总结已经抓住了精髓上面补充的细节可以帮助你更深入地理解并实际落地。如果还需要了解如何编写代码实现 UID 读取、MD5 计算或 RDP 设置我可以提供示例。4.示例代码以下代码演示了完整的出厂密钥生成与运行时的校验流程。为防篡改建议将密钥存储在OTP区域或受保护的Flash扇区。c#include string.h #define KEY_STORAGE_ADDR 0x0800FC00 // Flash存储密钥的地址 // 生成16字节MD5密钥基于UID 自定义盐 void Generate_Key(uint8_t *output_key) { uint32_t uid[3]; STM32_Get_UID(uid); // 读取96位UID uint8_t buffer[12 8] {0}; // 12字节UID 8字节自定义盐 memcpy(buffer, uid, 12); memcpy(buffer 12, YourSalt, 8); // 自定义盐 Compute_MD5(buffer, sizeof(buffer), output_key); // output_key为16字节 } // 出厂时执行一次生成密钥并写入Flash void Factory_Key_Generate(void) { uint8_t key[16]; Generate_Key(key); HAL_FLASH_Unlock(); // 先擦除存储扇区 FLASH_Erase_Sector(KEY_STORAGE_ADDR); // 写入密钥 for (int i 0; i 16; i) { HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, KEY_STORAGE_ADDR i, key[i]); } HAL_FLASH_Lock(); } // 每次启动时执行校验密钥不一致则死机 void Check_Key(void) { uint8_t calc_key[16]; uint8_t stored_key[16]; Generate_Key(calc_key); memcpy(stored_key, (uint8_t *)KEY_STORAGE_ADDR, 16); if (memcmp(calc_key, stored_key, 16) ! 0) { while (1) { // 校验失败进入死循环或触发复位 } } } // 主函数示例 int main(void) { HAL_Init(); // 判断是否首次运行例如检测特定Flash标志位 if (Is_First_Run()) { Factory_Key_Generate(); // 生成并写入密钥 Set_First_Run_Flag(); // 标记已初始化 } Check_Key(); // 每次启动校验 // 此处可设置RDP保护通常最后开启 // STM32_Set_RDP_Level(OB_RDP_LEVEL_1); while (1) { // 正常应用程序 } }⚠️ 重要提醒RDP设置时机建议在固件全部烧录、功能验证无误后作为量产的最后一步开启RDP。一旦开启调试接口将被禁用后续无法通过ST-Link/J-Link读取或烧录升级固件需通过Bootloader或空中升级(OTA)实现。密钥存储位置推荐使用OTPOne-Time Programmable区域存储密钥该区域只能编程一次且不可擦除安全性更高。RDP Level 2风险开启后永久禁用调试接口且不可逆转量产前务必测试所有功能否则芯片将变砖无法调试。以上示例代码可根据具体STM32系列微调。如需更高级的加密如AES硬件加密可参考STM32的CRYP模块或安全固件安装(SFI)等机制。5.AES加密1.概念和实现高级加密标准AES可以理解为一种能将你的数据锁起来并且目前公认很难被破解的“高强度锁”。它是一种对称加密算法意思是加密和解密使用的是同一把“钥匙”密钥。 AES 的核心密钥长度AES的安全性很大程度上取决于“钥匙”的长度主要分为三种AES-128使用128位密钥是目前最常见的选择。它在安全性和性能之间取得了良好的平衡。AES-192使用192位密钥安全性比AES-128更高但速度稍慢适用于对安全有更高要求的场景。AES-256使用256位密钥是AES家族中最安全的版本也是许多政府和金融机构的加密标准。选择哪种本质上是根据你的具体应用场景在安全性和加解密速度之间做权衡。⚙️ AES 是如何工作的AES的工作过程可以拆解为三个步骤你可以把它们想象成对数据“加扰”的三个阶段数据分块AES先把要加密的数据分成一个个固定大小的小块每个块是128位16字节。然后它会对每个小块分别进行加密处理。密钥扩展AES会将原始密钥扩展成一系列不同的子密钥用于后续的每一轮加密这个过程叫作“密钥扩展”。多轮加密对每个数据块进行若干轮10、12或14轮取决于密钥长度的复杂运算。每一轮都包含替换、移位、混合和密钥加等操作让原始数据和密钥充分“搅”在一起。️ 选择哪种工作模式AES在处理多个数据块时需要指定一种“工作模式”。这决定了如何将独立的块串联起来。以下是两种最常见模式的对比特性ECB电子密码本模式CBC密码块链模式原理每个数据块独立加密相同明文产生相同密文每个数据块加密前先与前一个密文块进行异或运算优点简单、高效可并行处理安全性高相同明文产生不同密文缺点不安全会泄露明文模式加密过程无法并行需要初始化向量推荐度强烈不推荐推荐使用此外还有CTR计数模式、GCM伽罗瓦/计数器模式等。其中GCM模式很受欢迎因为它不仅能加密还能同时做完整性校验。 在STM32上实现AESSTM32提供了两种实现AES的方式1. 硬件加速器AES外设很多STM32系列如STM32L4, STM32H7等内部集成了专用的AES硬件加速器。你可以把它理解为一个专门为AES算法设计的硬件模块。极致性能硬件加速器比纯软件实现快10到20倍。例如用128位密钥加密一个16字节的数据块硬件加速器仅需约51个时钟周期。低功耗硬件完成同样任务消耗的能耗远低于CPU。简化主控CPU只需将数据和密钥交给AES外设然后就可以去处理其他任务无需全程参与复杂的计算。2. 软件实现如果你的STM32型号没有AES硬件外设也可以使用纯软件算法库如mbedTLS来实现AES。这种方式更灵活不依赖特定硬件但加解密速度会慢很多并且会占用CPU大量时间。️ SAES更安全的AES版本在一些较新的STM32型号如STM32U5, STM32H5中ST公司还提供了一个更安全的AES版本称为SAESSecure AES。抵抗侧信道攻击AES硬件虽然快但运行时可能会通过功耗、电磁辐射等方式“泄露”密钥信息。SAES通过硬件设计上的优化可以有效抵抗这种攻击。硬件密钥SAES可以直接使用芯片内部硬件派生的密钥例如基于唯一ID派生的DHUK。这意味着你的密钥永远不会出现在软件中从根本上防止了密钥被读取的可能。 安全实践如何管理好你的密钥一个安全的加密系统密钥的管理比加密算法本身更重要。绝不硬编码千万不要在代码中直接将密钥写死为常量。攻击者一旦获得你的固件就能轻易找到这些密钥。密钥派生可以利用每颗芯片唯一的96位UID通过特定算法如哈希运算派生出唯一的密钥。这样每台设备的密钥都不同可以防止固件被批量克隆。安全存储对于SAES这类硬件可以直接使用硬件派生的密钥避免密钥暴露在软件中。对于普通AES应结合使用PCROP专有代码保护等功能将存储密钥的Flash区域保护起来禁止任何形式的读取。安全启动Secure Boot这是将AES用于固件保护最经典的应用。简单来说程序启动时会先运行一个不可更改的“引导程序”Bootloader它负责将Flash中加密的应用程序代码进行解密然后再跳转执行。这个过程确保了只有合法的固件才能被解密和运行。2.通俗来讲用最生活化的方式来理解 AES 加密。你可以把 AES 想象成一个“超级搅拌机”把你想保护的信息比如一段文字、一张图片放进去加上一把“钥匙”一按开关出来就变成一堆谁也看不懂的“乱码”。只有拿着同一把“钥匙”的人才能把这堆“乱码”重新变回原来的信息。 第一步把信息切成小块AES 这个搅拌机一次只能处理固定大小的原料。它会把你所有的信息像切香肠一样切成16 厘米16字节长的段。一段一段地搅。 第二步你需要一把钥匙这把钥匙就是你的“密码”。AES 提供了三种长度的钥匙128 位的钥匙相当于一个 16 位的数字密码强度足够日常用192 位的钥匙相当于 24 位密码更安全256 位的钥匙相当于 32 位密码银行/军方级别极难破解钥匙越长搅拌的轮数越多也就越安全但速度会稍微慢一点。 第三步搅拌过程这就是加密的核心把一段 16 字节的原料和你的钥匙一起放进搅拌机它会重复做10 ~ 14 轮取决于钥匙长度的混合操作每一轮都包括替换把每个字节按照一张秘密的替换表换成另一个字节就像用摩斯密码替换字母。移位把整段数据里的字节像排队一样左右移动位置。混合把这一行和那一列的数据混合起来让每个输出字节都依赖于所有输入字节。加钥匙把这一轮专用的子钥匙混进去。每一轮之后数据都变得更加“面目全非”。经过十几轮之后你根本看不出它和原来的数据有任何关系。 工作模式怎么处理多段数据因为信息被切成了很多段每一段都用同样的钥匙单独搅拌如果不做额外处理那么两个相同的段搅拌后会得到相同的乱码这就可能泄露信息。所以 AES 提供了几种“连接方式”ECB 模式最差每一段独立搅拌完全相同的内容出来完全相同的乱码。不推荐因为攻击者能看出规律。CBC 模式常见且安全先把当前段和上一段的搅拌结果混在一起再放进搅拌机。这样即使原始内容相同出来的乱码也不一样。第一段因为没有“上一段”需要额外加一个随机数叫“初始化向量”。除了 CBC还有更先进的GCM 模式它不仅能加密还能在搅拌时顺便贴上一个“防伪标签”用来验证数据没有被篡改。️ AES 到底有多安全目前没有公开的方法能直接破解 AES。最可行的攻击方式是“暴力尝试”也就是用所有可能的钥匙去试。对于 AES-128用全世界最快的计算机去试需要上亿亿亿年。对于 AES-256那是给担心量子计算机出现后的人准备的安全级别更高。所以只要你的钥匙足够随机不是 “123456” 这种并且妥善保管AES 就是目前最可靠的加密方法之一。 举个生活中的例子你要寄一个箱子给朋友但怕快递员偷看原始内容一张写有“明天见面”的纸条。AES 加密你把纸条放进一个AES 搅拌机用你们约定好的钥匙比如“苹果123”启动机器。出来的是一堆看起来像“#%*…8f3a”的乱码。运输快递员看到这堆乱码完全不知道是什么。解密你朋友拿到箱子后放进同一个AES 反向搅拌机输入相同的钥匙“苹果123”机器就把乱码恢复成“明天见面”。✅ 总结一句话AES 就是把你的数据切成小块用一把钥匙反复“搅拌、打乱、混合”很多轮最后变成一堆没有钥匙就绝对解不开的乱码。它是目前全世界应用最广泛、最可靠的加密算法之一。

更多文章