You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32(PlatformIO)中esp_aes_crypt_ecb最后块加密失败求助

问题:ESP32 AES-ECB加密最后一块数据异常

在ESP32(PlatformIO环境)中实现AES-ECB加密,明文长度31字符,需拆分为3个16字节块。前两块加密结果与在线工具一致,但最后一块加密失败,在线工具解密时提示“Given final block not properly padded”,且ESP32输出的最后块与在线工具结果不符。当前代码中最后一块按C/C++字符串规则以0填充。

相关代码

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "mbedtls/aes.h"

#include "esp_log.h"
#include "aes/esp_aes.h"
#include "mbedtls/base64.h"

#define ESP_AES_ENCRYPT 1

unsigned char key[33] = {'d', 'v', 'i', 'z', 'd', 'u', 'm', 'c', 's', 'm', 'v', 'l', 'v', 'f', 'd', 'v', 'b', 'j', 's', 'v', 'd', 'u', 'i', 'a', 's', 'r', 'u', 'i', 'a', 'e', 'u', 'y', '\0'};

void crypto_aes_ecb(char *input){
    static const char *TAG = "CryptTest";
    bool doEncrypt = true;
    uint16_t blockIndex = 0;
    uint16_t inputIndex = 0;
    uint16_t outputIndex = 0;
    unsigned char inputBlock[16] = {0};
    unsigned char output[17] = {0};
    unsigned char encrypted[255] = {0};
    esp_aes_context aes;

    esp_aes_init(&aes);
    esp_aes_setkey(&aes, (const unsigned char*) key, 256);
    inputIndex = 0;
    while(doEncrypt){
        for (blockIndex = 0; blockIndex < 16; blockIndex++){
            inputBlock[blockIndex] = input[blockIndex + (inputIndex * 16)];
            if(input[blockIndex + (inputIndex * 16)] == '\0'){
                for (; blockIndex < 16; blockIndex++){inputBlock[blockIndex] = 0;}
                doEncrypt = false;
                break;
            }
        }
        inputIndex++;
        inputBlock[16] = '\0';
        ESP_LOGI(TAG, "inputBlock = '%s'", inputBlock);
        esp_aes_crypt_ecb(&aes, ESP_AES_ENCRYPT, (const unsigned char *)inputBlock, output);
        for (blockIndex = 0; blockIndex < 16; blockIndex++){
            encrypted[blockIndex + (outputIndex * 16)] = output[blockIndex];
        }
        outputIndex++;
    }
    ESP_LOGW(TAG, "-----------------------------------------------------------------------------------");
    ESP_LOGW(TAG, "Key     : '%s'", key);
    ESP_LOGW(TAG, "Input   : '%s'", input);
    ESP_LOG_BUFFER_HEXDUMP(TAG, encrypted, 128, ESP_LOG_WARN);
    esp_aes_free(&aes);
}

void app_main(){
    crypto_aes_ecb("Testing encryption... Testing encryption...");
}

测试结果对比

  • ESP32输出加密HexDump:
ea 99 ca 39 2b 62 58 ce 71 5a 75 62 fe ac b7 f0 29 16 0c 1e 20 f5 ba 6f b0 7e c7 ee 0b eb 3a e3 89 51 f4 84 97 61 4b e8 66 22 78 72 60 0f 3e 2d
  • 在线工具加密结果:
EA 99 CA 39 2B 62 58 CE 71 5A 75 62 FE AC B7 F0 29 16 0C 1E 20 F5 BA 6F B0 7E C7 EE 0B EB 3A E3 3F 3E 65 3F 78 BA AD 13 AE 74 3E 99 44 1F E1 E5

问题根源与解决方案

问题根源

  1. 填充方式不匹配:在线工具默认采用PKCS#7填充,而当前代码使用Zero Padding(全0填充)。PKCS#7规则是:若数据块长度不足16字节,则填充16 - 剩余字节数个值为该数字的字节。31字节明文的最后一块需要填充1个0x01,而非全0。
  2. 字符串处理逻辑错误:代码读取到字符串结束符\0时,会将该\0写入块中再填充0,导致最后一块的第一个字节是无效结束符,而非正确的明文末尾字符加PKCS#7填充。
  3. 全局变量风险:原代码中esp_aes_context为全局变量,多任务环境下易引发上下文冲突。
  4. 密钥长度冗余:AES-256需要32字节密钥,原key数组末尾的\0属于冗余字符,会导致密钥实际传入长度异常。

修复代码

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "mbedtls/aes.h"

#include "esp_log.h"
#include "aes/esp_aes.h"
#include "mbedtls/base64.h"

#define ESP_AES_ENCRYPT 1

// AES-256密钥:32字节,无末尾\0
unsigned char key[32] = {'d','v','i','z','d','u','m','c','s','m','v','l','v','f','d','v','b','j','s','v','d','u','i','a','s','r','u','i','a','e','u','y'};

void crypto_aes_ecb(char *input){
    static const char *TAG = "CryptTest";
    esp_aes_context aes;
    unsigned char encrypted[255] = {0};
    size_t inputLen = strlen(input);
    // 计算总块数:向上取整
    size_t totalBlocks = (inputLen + 15) / 16;
    unsigned char inputBlock[16] = {0};
    unsigned char output[16] = {0};

    esp_aes_init(&aes);
    esp_aes_setkey(&aes, key, 256);

    for(size_t blockIdx = 0; blockIdx < totalBlocks; blockIdx++){
        size_t startPos = blockIdx * 16;
        // 计算当前块需要复制的有效字节数
        size_t copyLen = (startPos + 16) <= inputLen ? 16 : (inputLen - startPos);
        memset(inputBlock, 0, sizeof(inputBlock));
        memcpy(inputBlock, input + startPos, copyLen);

        // 应用PKCS#7填充
        int padLen = 16 - copyLen;
        memset(inputBlock + copyLen, padLen, padLen);

        ESP_LOGI(TAG, "Block %d input (hex):", blockIdx);
        ESP_LOG_BUFFER_HEX(TAG, inputBlock, 16, ESP_LOG_INFO);

        // 加密当前块
        esp_aes_crypt_ecb(&aes, ESP_AES_ENCRYPT, inputBlock, output);
        memcpy(encrypted + blockIdx*16, output, 16);
    }

    ESP_LOGW(TAG, "-----------------------------------------------------------------------------------");
    ESP_LOGW(TAG, "Key length: %zu bytes", sizeof(key));
    ESP_LOGW(TAG, "Input   : '%s' (length: %zu)", input, inputLen);
    ESP_LOG_BUFFER_HEXDUMP(TAG, encrypted, totalBlocks*16, ESP_LOG_WARN);
    esp_aes_free(&aes);
}

void app_main(){
    crypto_aes_ecb("Testing encryption... Testing encryption...");
}

修复说明

  • PKCS#7填充:根据块剩余长度填充对应数值的字节,确保与在线工具填充规则一致。
  • 字符串长度计算:通过strlen获取明文真实长度,避免被\0提前截断。
  • 局部上下文:将esp_aes_context改为函数内局部变量,避免多任务冲突。
  • 密钥修正:移除密钥数组末尾的\0,确保AES-256使用正确的32字节密钥。

内容的提问来源于stack exchange,提问作者Geir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 10:44:54