1113 字
6 分钟
STM32 软件实现 AES 加解密:原理、实现与优化
1. 为什么选择软件实现 AES?
AES(Advanced Encryption Standard,高级加密标准)是目前应用最广泛的对称加密算法。虽然许多中高端 STM32 芯片(如 STM32F4/L4 系列的某些型号)带有硬件 CRYP 加速器,但在以下场景软件实现更具优势:
- 芯片兼容性:适用于不带硬件加密模块的低成本型号(如 STM32F1/F0 系列)。
- 代码可移植性:方便在不同架构(如 RISC-V 或 AVR)之间复用。
- 自定义安全性:便于实现非标准长度的密钥或魔改特定轮次。
2. AES 加解密核心流程
AES 处理的基本单位是 128 位(16 字节) 的数据块。根据密钥长度(128/192/256 位),算法会执行 10 到 14 轮不等的处理。
核心操作步骤:
- 密钥扩展 (Key Expansion):将初始密钥扩展为多个轮密钥。
- 字节替换 (SubBytes):通过 S-Box(置换盒)实现非线性映射,防止差分攻击。
- 行移位 (ShiftRows):将状态矩阵的行进行循环移位。
- 列混合 (MixColumns):通过有限域乘法对列进行混淆。
- 轮密钥加 (AddRoundKey):将当前状态与对应的轮密钥进行异或。
3. 工程代码实现
头文件 (aes.h)
定义支持的加密模式和接口。在嵌入式中,推荐使用 CBC 模式,因为它比 ECB 模式安全性更高。
#ifndef _AES_H#define _AES_H
#include <stdint.h>
#define AES_KEY_LENGTH 128 // 支持 128, 192, 256 位#define AES_MODE_CBC 1 // 密码分组链接模式(推荐)
// 初始化:密钥扩展void aes_init(const uint8_t *pKey);
// 加密:输入数据长度必须是16字节的整数倍void aes_encrypt(const uint8_t *pPlainText, uint8_t *pCipherText, uint32_t nDataLen, const uint8_t *pIV);
// 解密void aes_decrypt(const uint8_t *pCipherText, uint8_t *pPlainText, uint32_t nDataLen, const uint8_t *pIV);
#endif核心源文件 (aes.c)
为了保证执行效率,S-Box 通常以查找表的形式存在。
#include "aes.h"#include <string.h>
// 常量定义#define Nb 4 // 状态矩阵的列数#if AES_KEY_LENGTH == 128 #define Nk 4 #define Nr 10#elif AES_KEY_LENGTH == 192 #define Nk 6 #define Nr 12#else #define Nk 8 #define Nr 14#endif
// 静态查找表:S-Box (节省空间建议将其放入 Flash)static const uint8_t sBox[256] = { /* ... 0x63, 0x7c, 0x77 ... */ };static const uint8_t invSBox[256] = { /* ... 0x52, 0x09, 0x6a ... */ };
// 存储扩展后的轮密钥static uint8_t g_roundKeyTable[4 * Nb * (Nr + 1)];
// 核心函数:密钥扩展void aes_init(const uint8_t *pKey) { // 实现详见 AES 标准算法库,将初始密钥映射至 g_roundKeyTable}
// 分组加密逻辑 (CBC 模式)void aes_encrypt(const uint8_t *pPlainText, uint8_t *pCipherText, uint32_t nDataLen, const uint8_t *pIV) { uint8_t tempIV[16]; memcpy(tempIV, pIV, 16);
for (uint32_t i = 0; i < nDataLen; i += 16) { // CBC 模式:加密前先与前一组密文(或IV)异或 for (int j = 0; j < 16; j++) { pCipherText[i + j] = pPlainText[i + j] ^ tempIV[j]; }
// 调用单个 Block 的加密函数 block_encrypt(&pCipherText[i]);
// 更新 IV 为当前密文 memcpy(tempIV, &pCipherText[i], 16); }}4. 针对 STM32 的性能优化建议
在嵌入式开发中,资源非常宝贵。以下是几个实用的优化方向:
- Flash vs RAM:
S-Box 和 Rcon 等常量数组应使用
const关键字修饰,确保它们存储在 Flash 空间而非消耗昂贵的 RAM。 - T-Table 优化: 如果 Flash 空间充裕,可以使用预计算的 T-Table(将 SubBytes、ShiftRows、MixColumns 合并为 4 个查找表),这能将加密速度提升 3-5 倍。
- 字对齐访问:
STM32 是 32 位架构。在异或(XOR)操作时,将
uint8_t指针转换为uint32_t指针进行批量异或,可以显著减少指令周期。 - 循环展开 (Loop Unrolling):
对于
Nr轮的循环,适当展开内层循环可以减少跳转指令的开销,这在 F0/F1 等低频芯片上效果明显。
5. 快速上手示例
int main(void) { // 准备 16 字节对齐的数据 uint8_t key[16] = "stm32_aes_key_v1"; uint8_t iv[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; uint8_t data[16] = "Hello STM32 AES"; uint8_t cipher[16]; uint8_t plain[16];
// 初始化算法 aes_init(key);
// 执行加密 aes_encrypt(data, cipher, 16, iv);
// 执行解密 aes_decrypt(cipher, plain, 16, iv);
// 通过串口打印输出对比结果... while(1);}6. 结论
软件实现 AES 虽然在吞吐量上不如硬件加速器,但其极高的灵活性使其成为嵌入式开发者的必备“工具箱”成员。通过合理的查找表和字对齐优化,在 STM32F103 (72MHz) 上加解密一张小型的传感器数据帧仅需几百微秒,完全满足大部分物联网(IoT)安全通信的需求。
STM32 软件实现 AES 加解密:原理、实现与优化
https://hw.rscclub.website/posts/stm32rjaesss/