1555 字
8 分钟
STM32 软件实现AES加解密

在嵌入式系统中,尤其是STM32等微控制器中,AES加密算法常用于保护通信数据或存储的数据安全。尽管STM32提供硬件AES加速模块,但在某些情况下,使用软件实现AES加解密可以更加灵活且适合某些应用。本文将介绍如何在STM32平台上使用软件实现AES加密和解密操作。

1. AES加解密流程#

AES(高级加密标准)是一个对称加密算法,通过扩展密钥生成多个轮次密钥,然后通过若干轮的加密操作,最终生成密文。解密时则是反向的过程,通过轮次密钥恢复出明文。

在软件实现中,AES加解密的主要步骤包括:

  1. 密钥扩展:将初始的128位、192位或256位密钥扩展为多个轮次密钥。
  2. 加密过程:通过多个轮次的加密操作,包括字节替换、行移位、列混合等。
  3. 解密过程:解密过程是加密过程的反向,包括逆字节替换、逆行移位、逆列混合等。

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. 核心功能说明#

  1. 密钥扩展

    • aes_init函数中,根据指定的密钥长度,执行密钥扩展操作,将初始密钥转换为多个轮次的子密钥。这个过程是AES加密的关键步骤。
  2. 字节替换(SubBytes)

    • 使用S-box进行字节替换操作,增加加密的复杂度和安全性。加密和解密都使用S-box,解密时使用的是逆S-box。
  3. 行移位(ShiftRows)

    • 该操作将状态矩阵的行进行循环左移。加密和解密的行移位方向不同,解密时行数据需要逆向移位。
  4. 列混合(MixColumns)

    • 列混合操作将每一列的数据进行变换,增强加密的扩散性。解密时使用逆列混合。
  5. 加解密循环(Round)

    • 主要的加密和解密操作通过多轮(Nr轮)处理进行。每一轮的操作包括字节替换、行移位、列混合以及与子密钥的异或操作。
  6. 加密模式

    • 支持ECB模式CBC模式。在CBC模式中,每个数据块的加密都依赖于前一个密文块,从而增加了安全性。

4. 优化建议#

  1. 提高执行效率:可以使用DMA(直接内存访问)来传输大数据块,减少CPU的负担。
  2. 内存优化:在嵌入式系统中,尽量减少内存占用,可以通过合并函数和使用宏来减少内存使用。
  3. 数据对齐:确保数据在内存中的对齐,可以提高访问速度。
  4. 性能测试:通过在不同的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支持的微控制器,软件实现是一种可靠且高效的选择。

STM32 软件实现AES加解密
https://hw.rscclub.website/posts/stm32rjaesss/
作者
杨月昌
发布于
2018-06-16
许可协议
CC BY-NC-SA 4.0