1555 字
8 分钟
STM32 软件实现AES加解密
在嵌入式系统中,尤其是STM32等微控制器中,AES加密算法常用于保护通信数据或存储的数据安全。尽管STM32提供硬件AES加速模块,但在某些情况下,使用软件实现AES加解密可以更加灵活且适合某些应用。本文将介绍如何在STM32平台上使用软件实现AES加密和解密操作。
1. AES加解密流程
AES(高级加密标准)是一个对称加密算法,通过扩展密钥生成多个轮次密钥,然后通过若干轮的加密操作,最终生成密文。解密时则是反向的过程,通过轮次密钥恢复出明文。
在软件实现中,AES加解密的主要步骤包括:
- 密钥扩展:将初始的128位、192位或256位密钥扩展为多个轮次密钥。
- 加密过程:通过多个轮次的加密操作,包括字节替换、行移位、列混合等。
- 解密过程:解密过程是加密过程的反向,包括逆字节替换、逆行移位、逆列混合等。
2. AES软件加解密实现
头文件(aes.h
)
#ifndef _AES_H
#define _AES_H
#define AES_KEY_LENGTH 128 // 密钥长度,支持128, 192, 256位
#define AES_MODE_ECB 0 // 电子密码本模式
#define AES_MODE_CBC 1 // 密码分组链接模式
#define AES_MODE AES_MODE_CBC // 默认加密模式
// 函数声明
void aes_init(const void *pKey); // 密钥初始化
void aes_encrypt(const unsigned char *pPlainText, unsigned char *pCipherText, unsigned int nDataLen, const unsigned char *pIV); // 数据加密
void aes_decrypt(const unsigned char *pCipherText, unsigned char *pPlainText, unsigned int nDataLen, const unsigned char *pIV); // 数据解密
#endif /* _AES_H */
源文件(aes.c
)
#include "aes.h"
#include "string.h"
// 定义密钥扩展等
#define Nk (AES_KEY_LENGTH / 32) // 以“字”(4字节)为单位的密钥长度
#define Nb 4 // 数据块大小,固定为4
// 设置加密轮数
#if AES_KEY_LENGTH == 128
#define Nr 10
#elif AES_KEY_LENGTH == 192
#define Nr 12
#elif AES_KEY_LENGTH == 256
#define Nr 14
#else
#error AES_KEY_LENGTH must be 128, 192 or 256 BOOLs!
#endif
// S-box表,用于加密和解密操作
static const unsigned char sBox[256] = { ... }; // S-box定义省略
static const unsigned char invSBox[256] = { ... }; // 反向S-box定义省略
// AES子密钥表
static unsigned char g_roundKeyTable[4 * Nb * (Nr + 1)];
static void xor_bytes(unsigned char *pData1, const unsigned char *pData2, unsigned int nCount) {
for (unsigned int i = 0; i < nCount; i++) {
pData1[i] ^= pData2[i];
}
}
// 旋转一个字
static void rotation_word(unsigned char *pWord) {
unsigned char temp = pWord[0];
pWord[0] = pWord[1];
pWord[1] = pWord[2];
pWord[2] = pWord[3];
pWord[3] = temp;
}
// 初始化AES
void aes_init(const void *pKey) {
// 密钥扩展
unsigned char i;
unsigned char *pRoundKey;
unsigned char Rcon[4] = {0x01, 0x00, 0x00, 0x00};
// 将原始密钥复制到密钥表
memcpy(g_roundKeyTable, pKey, 4 * Nk);
pRoundKey = &g_roundKeyTable[4 * Nk];
// 扩展密钥
for (i = Nk; i < Nb * (Nr + 1); pRoundKey += 4, i++) {
memcpy(pRoundKey, pRoundKey - 4, 4);
if (i % Nk == 0) {
rotation_word(pRoundKey);
sub_bytes(pRoundKey, 4, 0);
xor_bytes(pRoundKey, Rcon, 4);
Rcon[0] = gf_mult_by02(Rcon[0]);
}
else if (Nk > 6 && i % Nk == Nb) {
sub_bytes(pRoundKey, 4, 0);
}
xor_bytes(pRoundKey, pRoundKey - 4 * Nk, 4);
}
}
// AES加密
void aes_encrypt(const unsigned char *pPlainText, unsigned char *pCipherText, unsigned int nDataLen, const unsigned char *pIV) {
unsigned int i;
// 拷贝数据
if (pPlainText != pCipherText) {
memcpy(pCipherText, pPlainText, nDataLen);
}
// 数据块加密
for (i = nDataLen / (4 * Nb); i > 0; i--, pCipherText += 4 * Nb) {
#if AES_MODE == AES_MODE_CBC
xor_bytes(pCipherText, pIV, 4 * Nb);
#endif
block_encrypt(pCipherText); // 加密单个数据块
pIV = pCipherText;
}
}
// AES解密
void aes_decrypt(const unsigned char *pCipherText, unsigned char *pPlainText, unsigned int nDataLen, const unsigned char *pIV) {
unsigned int i;
// 拷贝数据
if (pPlainText != pCipherText) {
memcpy(pPlainText, pCipherText, nDataLen);
}
// 从最后一块数据开始解密
pPlainText += nDataLen - 4 * Nb;
for (i = nDataLen / (4 * Nb); i > 0; i--, pPlainText -= 4 * Nb) {
block_decrypt(pPlainText); // 解密单个数据块
#if AES_MODE == AES_MODE_CBC
if (i == 1) { // 最后一块数据
xor_bytes(pPlainText, pIV, 4 * Nb);
} else {
xor_bytes(pPlainText, pPlainText - 4 * Nb, 4 * Nb);
}
#endif
}
}
3. 核心功能说明
密钥扩展:
- 在
aes_init
函数中,根据指定的密钥长度,执行密钥扩展操作,将初始密钥转换为多个轮次的子密钥。这个过程是AES加密的关键步骤。
- 在
字节替换(SubBytes):
- 使用S-box进行字节替换操作,增加加密的复杂度和安全性。加密和解密都使用S-box,解密时使用的是逆S-box。
行移位(ShiftRows):
- 该操作将状态矩阵的行进行循环左移。加密和解密的行移位方向不同,解密时行数据需要逆向移位。
列混合(MixColumns):
- 列混合操作将每一列的数据进行变换,增强加密的扩散性。解密时使用逆列混合。
加解密循环(Round):
- 主要的加密和解密操作通过多轮(
Nr
轮)处理进行。每一轮的操作包括字节替换、行移位、列混合以及与子密钥的异或操作。
- 主要的加密和解密操作通过多轮(
加密模式:
- 支持ECB模式和CBC模式。在CBC模式中,每个数据块的加密都依赖于前一个密文块,从而增加了安全性。
4. 优化建议
- 提高执行效率:可以使用
DMA
(直接内存访问)来传输大数据块,减少CPU的负担。 - 内存优化:在嵌入式系统中,尽量减少内存占用,可以通过合并函数和使用宏来减少内存使用。
- 数据对齐:确保数据在内存中的对齐,可以提高访问速度。
- 性能测试:通过在不同的STM32平台上测试加解密的性能,确保实现的高效性。
5. 使用示例
int main(void) {
unsigned char buf[16], saveBuf[16], decryptBuf[16];
unsigned char AES128key[16] = "123456789abcdefa"; // 密钥
unsigned char AES_IV[16] = "0102030405123456"; // 向量表
// AES初始化
aes_init(AES128key);
// 填充数据
for (int i = 0; i < sizeof(buf); i++) {
buf[i] = i;
}
// 加密
aes_encrypt(buf, saveBuf, sizeof(buf), AES_IV);
// 解密
aes_decrypt(saveBuf, decryptBuf, sizeof(buf), AES_IV);
// 检查解密后的数据与原数据是否一致
if (memcmp(buf, decryptBuf, sizeof(buf)) == 0) {
// 解密成功,执行相应操作
}
while(1) {
// LED闪烁等其他应用操作
}
}
6. 结论
通过以上代码实现,STM32可以利用软件方式进行AES加解密操作。软件AES实现提供了较好的灵活性,能够满足大部分嵌入式应用的加密需求。对于没有硬件AES支持的微控制器,软件实现是一种可靠且高效的选择。