你的STM32项目安全吗?用芯片唯一ID实现简易防抄板与软件授权(附代码)
基于STM32唯一ID构建硬件级软件保护方案实战指南在嵌入式产品开发中保护知识产权和防止软件被非法复制是开发者面临的重要挑战。许多初创团队投入大量精力开发的固件可能因为缺乏基本保护措施而被轻易复制到其他硬件上运行。STM32系列微控制器内置的96位唯一设备标识符(Unique Device ID)为解决这一问题提供了硬件级支持。1. STM32唯一ID的核心特性与应用价值STM32的UID是一个出厂时烧录的96位唯一标识符具有不可修改、全球唯一的特性。根据ST官方文档说明这个ID的误差率低于10^-15几乎可以视为绝对唯一标识。与软件生成的序列号不同UID是芯片物理层面的属性无法通过常规手段篡改。典型应用场景包括软件授权绑定将软件与特定硬件设备关联防抄板保护防止固件被复制到其他硬件运行设备身份认证为物联网设备提供硬件级身份凭证安全密钥生成作为加密算法的唯一性种子注意不同STM32系列的UID地址可能不同使用前务必查阅对应型号的参考手册2. 跨平台UID读取方法与统一接口设计虽然UID读取原理简单但不同STM32系列的存储地址存在差异。下面是一个兼容多系列的统一读取实现// stm32_uid.h #pragma once #include stdint.h #ifdef STM32F1 #define UID_BASE 0x1FFFF7E8 #elif defined(STM32F2) || defined(STM32F4) #define UID_BASE 0x1FFF7A10 #elif defined(STM32F3) #define UID_BASE 0x1FFFF7AC #elif defined(STM32F7) #define UID_BASE 0x1FF0F420 #else #error Unsupported STM32 series #endif void read_uid(uint8_t uid[12]);// stm32_uid.c #include stm32_uid.h void read_uid(uint8_t uid[12]) { const uint8_t *p (const uint8_t *)UID_BASE; for(int i0; i12; i) { uid[i] *p; } }主要STM32系列的UID基地址对比表芯片系列UID基地址数据宽度STM32F10x1FFFF7E896位STM32F2/F40x1FFF7A1096位STM32F30x1FFFF7AC96位STM32F70x1FF0F42096位STM32H70x1FF1E80096位3. 基于UID的软件授权方案实现单纯的UID读取并不能提供有效的保护需要结合适当的算法将其转化为可验证的授权机制。下面介绍三种实用方案3.1 简单校验和方案// 生成简单的校验和密钥 uint32_t generate_checksum(const uint8_t uid[12]) { uint32_t sum 0; for(int i0; i12; i) { sum uid[i]; } return sum ^ 0x55AA55AA; // 加入固定掩码 } // 在软件中验证 bool verify_license() { uint8_t uid[12]; read_uid(uid); uint32_t expected generate_checksum(uid); uint32_t stored read_flash_checksum(); // 从Flash读取存储的值 return expected stored; }3.2 哈希加密方案#include mbedtls/md5.h // 生成MD5哈希作为设备指纹 void generate_device_fingerprint(const uint8_t uid[12], uint8_t fingerprint[16]) { mbedtls_md5_context ctx; mbedtls_md5_init(ctx); mbedtls_md5_starts(ctx); mbedtls_md5_update(ctx, uid, 12); mbedtls_md5_update(ctx, (const uint8_t*)SALT1234, 8); // 加入盐值 mbedtls_md5_finish(ctx, fingerprint); mbedtls_md5_free(ctx); }3.3 激活码机制实现// 生成基于UID的激活码 void generate_activation_code(const uint8_t uid[12], char code[25]) { uint32_t part1 *(uint32_t*)uid; uint32_t part2 *(uint32_t*)(uid4); uint32_t part3 *(uint32_t*)(uid8); part1 (part1 ^ 0xDEADBEEF) * 0x12345679; part2 (part2 ^ 0xCAFEBABE) * 0x87654321; part3 (part3 ^ 0xBAADF00D) * 0x13579BDF; snprintf(code, 25, %08X-%08X-%08X, part1, part2, part3); } // 验证激活码 bool verify_activation_code(const char* input_code) { uint8_t uid[12]; read_uid(uid); char expected_code[25]; generate_activation_code(uid, expected_code); return strcmp(input_code, expected_code) 0; }4. 进阶安全增强策略基础UID方案虽然有效但仍有被破解的风险。以下是几种增强安全性的方法4.1 动态密钥派生// 基于UID和运行时信息的动态密钥生成 void generate_dynamic_key(const uint8_t uid[12], uint8_t dynamic_key[16]) { uint32_t tick HAL_GetTick(); // 获取系统tick值 uint32_t rnd HAL_GetRandomNumber(); // 获取随机数 mbedtls_md5_context ctx; mbedtls_md5_init(ctx); mbedtls_md5_starts(ctx); mbedtls_md5_update(ctx, uid, 12); mbedtls_md5_update(ctx, (uint8_t*)tick, 4); mbedtls_md5_update(ctx, (uint8_t*)rnd, 4); mbedtls_md5_finish(ctx, dynamic_key); mbedtls_md5_free(ctx); }4.2 分块验证机制将验证逻辑分散在代码多处增加破解难度// 在main函数初始化时验证第一部分 bool verify_part1() { uint8_t uid[12]; read_uid(uid); return (uid[0] uid[5] uid[11]) expected_sum1; } // 在关键功能前验证第二部分 bool verify_part2() { uint8_t uid[12]; read_uid(uid); uint32_t hash uid[3] | (uid[7]8) | (uid[9]16); return (hash % 17) expected_remainder; } // 在定时中断中随机验证 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static int counter 0; if(counter 1000) { counter 0; if(!verify_random_part()) { system_lock(); } } }4.3 安全存储方案将关键验证信息存储在芯片的写保护区域或OTP(One-Time Programmable)区域// 写入OTP区域示例 void write_otp(uint32_t data) { HAL_FLASH_Unlock(); __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); FLASH_OBProgramInitTypeDef ob; ob.OptionType OPTIONBYTE_DATA; ob.DATAAddress 0x1FFF7800; // OTP区域地址 ob.DATAData data; HAL_FLASHEx_OBProgram(ob); HAL_FLASH_Lock(); } // 读取OTP区域 uint32_t read_otp() { return *(__IO uint32_t*)0x1FFF7800; }5. 方案评估与选择建议不同保护方案在安全性和实现复杂度上各有优劣保护方案对比表方案类型安全性实现难度适用场景破解难度简单校验和★★☆★☆☆低价值产品低哈希加密★★★★★☆中等价值产品中激活码机制★★★☆★★★需要分发的商业产品中高动态密钥★★★★★★★☆高安全性要求产品高分块验证★★★★☆★★★★极高价值产品极高选择方案时应考虑产品的商业价值和复制可能带来的损失预期的产品生命周期开发团队的安全技术储备终端用户的使用体验要求在实际项目中我通常会采用组合策略使用哈希加密作为基础验证在关键功能处加入动态检查同时配合OTP存储部分关键信息。这种分层防御的方式能在开发复杂度和安全性之间取得较好平衡。