AES-128-ECB 在 esp32 中加密并在 Node.js 中解密

问题描述 投票:0回答:1

我想在 esp32 中创建一个带有 aes-128-ecb 加密查询参数的 URL,并在 node.js 中对其进行解密。这是我在 ESP32 上的方法:

uint64_t variableToEncrypt = 27483611521760; // it comes as a 64 bit unsgined int from efuse
std::string AES_KEY = "0123456789ABCDEF"; // hex format
const char* serverBaseURL = "https://testNodeJS.com/";

void generateURL(){
    // Determine the size of the variable
    int bufferSize = snprintf(NULL, 0, "%" PRIu64, variableToEncrypt);
    // Make static arrays
    char URL_BUFFER[270];
    char stringVariableBuffer[bufferSize + 1];

    // Convert the variableToEncrypt to a string
    snprintf(stringVariableBuffer, bufferSize + 1, "%" PRIu64, variableToEncrypt);

    // Encrypt the variable
    std::string encryptedHex = cipher.encrypt(stringVariableBuffer, AES_KEY);

    // Construct the URL
    snprintf(URL_BUFFER, sizeof(URL_BUFFER), "%testDecrypt?key=%s", serverBaseURL, encryptedHex.c_str());

    Serial.printf("Raw variable (uint64_t): %llu\n", variableToEncrypt);
    Serial.printf("Variable (string): %s\n", stringVariableBuffer);
    Serial.printf("Encrypted Hex: %s\n", encryptedHex.c_str());
    Serial.printf("URL Buffer: %s\n", URL_BUFFER);
}

该函数产生以下输出:

Raw variable(uint64_t): 27483611521760
Variable(string): 27483611521760
Encrypted Hex: 4221458b03cc8692c969e5aa9aac4f31
URL Buffer: https://testNodeJS.com/testDecrypt?key=4221458b03cc8692c969e5aa9aac4f31

我用在线工具确认没问题。它能够将加密的十六进制字符串解密回原始变量字符串。

现在,如果我单击此链接,它将带我进入我想要解密的 Node.js 测试环境

const crypto = require('crypto');

function decryptAES(ciphertextHex, key) {
    try{
        // Convert ciphertext from hex string to buffer
        const ciphertext = Buffer.from(ciphertextHex, 'hex');
        // Create decipher object
        const decipher = crypto.createDecipheriv('aes-128-ecb', Buffer.from(key, 'hex'), Buffer.alloc(0));
        // Decrypt ciphertext
        let decrypted = decipher.update(ciphertext);
        decrypted = Buffer.concat([decrypted, decipher.final()]);
        // Convert decrypted buffer to string
        const plaintext = decrypted.toString();
        return plaintext;
    }catch(error){
        console.log(error);
        return null;
    }
}


app.get('/testDecrypt', (req, res) => {
    const decryptedVariable = decryptAES(req.query.key, process.env.AES_KEY);
    console.log('Decrypted variable:', decryptedVariable , "Raw query variable: ", req.query.key, "AES key: ", process.env.AES_KEY);

    if(!decryptedVariable){
        return res.status(401).json({ message: 'Wrong variable!' });
    }
    return res.json({message:"ok"});
});

但是node.js的decryptAES函数总是返回null并出现以下错误:

RangeError: Invalid key length
    at Decipheriv.createCipherBase (node:internal/crypto/cipher:121:19)
    at Decipheriv.createCipherWithIV (node:internal/crypto/cipher:140:3)
    at new Decipheriv (node:internal/crypto/cipher:289:3)
    at Object.createDecipheriv (node:crypto:154:10)
    at decryptAES (/opt/render/project/src/server.js:44:33)
    at /opt/render/project/src/server.js:74:29
    at Layer.handle [as handle_request] (/opt/render/project/src/node_modules/express/lib/router/layer.js:95:5)
    at next (/opt/render/project/src/node_modules/express/lib/router/route.js:144:13)
    at Route.dispatch (/opt/render/project/src/node_modules/express/lib/router/route.js:114:3)
    at Layer.handle [as handle_request] (/opt/render/project/src/node_modules/express/lib/router/layer.js:95:5) {
  code: 'ERR_CRYPTO_INVALID_KEYLEN'
}
Decrypted variable: null Raw query variable:  4221458b03cc8692c969e5aa9aac4f31 AES key:  0123456789ABCDEF

process.env.AES_KEY 在 env 文件中存储如下:AES_KEY=0123456789ABCDEF

这是我在 esp32 上使用 mbedtls 的加密函数

#include "mbedtls/aes.h"

std::string CryptoCipher::encrypt(const std::string& plaintext, const std::string& key) {
    mbedtls_aes_context aes_ctx;
    mbedtls_aes_init(&aes_ctx);

    // Set encryption key
    mbedtls_aes_setkey_enc(&aes_ctx, reinterpret_cast<const unsigned char*>(key.c_str()), 128);

    // Pad plaintext to block size if necessary
    int padded_length = ((plaintext.length() + 15) / 16) * 16;
    std::string padded_plaintext = plaintext;
    padded_plaintext.resize(padded_length, '\0');

    // Allocate memory for ciphertext
    std::string ciphertext(padded_length, '\0');

    // Perform encryption
    for (size_t i = 0; i < padded_length; i += 16) {
        mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, reinterpret_cast<const unsigned char*>(padded_plaintext.c_str() + i), reinterpret_cast<unsigned char*>(ciphertext.data() + i));
    }

    // Convert ciphertext to hex string
    std::string hex_string;
    hex_string.reserve(ciphertext.length() * 2);
    for (unsigned char c : ciphertext) {
        char buf[3];
        snprintf(buf, sizeof(buf), "%02x", static_cast<unsigned int>(c));
        hex_string.append(buf);
    }

    mbedtls_aes_free(&aes_ctx);
    
    return hex_string;
}
node.js aes arduino-esp32 node-crypto mbedtls
1个回答
0
投票

密文的解密表明加密中使用了零填充,请参见例如这里与 CyberChef 一起。

相比之下,NodeJS 默认使用 PKCS#7 填充;不支持零填充。对于使用 NodeJS 解密,必须禁用标准 PKCS#7 填充。如果必须执行取消填充,则必须手动删除填充字节。

此外,密钥必须是 ASCII/UTF-8 编码而不是十六进制解码(如评论中已指出)。

可能的修复:

...
const decipher = crypto.createDecipheriv('aes-128-ecb', Buffer.from(key, 'utf8'), Buffer.alloc(0)); // Fix 1: ASCII/UTF8 encode key
decipher.setAutoPadding(false);                                                                     // Fix 2: disable PKCS#7 default padding
...
return plaintext.replace(/\x00+$/g, '');                                                            // Fix 3: unpad (if required) 
...

请注意,与 PKCS#7 填充相比,零填充是不可靠的,即通常无法区分末尾的 0x00 填充字节和常规 0x00 字节。

© www.soinside.com 2019 - 2024. All rights reserved.