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

请求:用OpenSSL实现与Node.js CryptoJS兼容的AES加密C++代码

实现与Node.js CryptoJS AES加密逻辑一致的C++ OpenSSL代码

原Node.js CryptoJS加密逻辑

原代码逻辑为:将10位数字消息与指定的SecuritySalt拼接后,使用32位字符串secret_key进行AES加密,输出Base64格式密文。代码如下:

var CryptoJS = require("crypto-js");
const SecuritySalt = "Amidst the bustling city streets, a solitary figure found solace in the melody of raindrops dancing upon the cobblestones.sdwe2q";
const secret_key = "Whispersofthewindechoedthroughth";

const encryptData = (message) => {
    return CryptoJS.AES.encrypt(
        SecuritySalt + message,
        secret_key 
     ).toString();
};

let message = "1111111111";
console.log("encrypt message :" + encryptData(message));

对应的解密代码:

const secret_key = "Whispersofthewindechoedthroughth";

const decryptData = (cipherText) => {
    const bytes = CryptoJS.AES.decrypt(cipherText, secret_key);
    let message = bytes.toString(CryptoJS.enc.Utf8);
    return message;
};

// 假设cipherText是加密后的Base64字符串
console.log("decrypt: " + decryptData(cipherText));

原C++实现的问题

你之前的实现错误在于偏离了CryptoJS的默认行为:

  • CryptoJS使用EVP_BytesToKey算法(MD5哈希,迭代1次)从字符串密钥生成AES密钥和IV,而非PKCS5_PBKDF2_HMAC
  • CryptoJS会生成随机8字节盐,并用Salted__<盐><密文>的格式打包后再做Base64编码,而非使用全0IV和直接编码密文

正确的C++ OpenSSL实现代码

以下代码严格遵循CryptoJS的默认AES加密逻辑:

#include <iostream>
#include <string>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

// Base64编码函数(无换行符,匹配CryptoJS输出格式)
std::string base64_encode(const unsigned char* data, size_t length) {
    BIO *bio, *b64;
    BUF_MEM *bufferPtr;

    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);

    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
    BIO_write(bio, data, length);
    BIO_flush(bio);
    BIO_get_mem_ptr(bio, &bufferPtr);
    BIO_set_close(bio, BIO_NOCLOSE);

    std::string encoded(bufferPtr->data, bufferPtr->length);
    BIO_free_all(bio);

    return encoded;
}

std::string encrypt_aes_cryptojs_compatible(const std::string& plaintext, const std::string& password) {
    // 生成随机8字节盐(CryptoJS默认行为)
    unsigned char salt[8];
    if (!RAND_bytes(salt, sizeof(salt))) {
        throw std::runtime_error("Failed to generate random salt");
    }

    // 使用EVP_BytesToKey生成AES-256-CBC密钥和IV
    unsigned char key[32], iv[16];
    int key_len = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), salt, 
                                 reinterpret_cast<const unsigned char*>(password.c_str()), password.length(), 
                                 1, key, iv);
    if (key_len != 32) {
        throw std::runtime_error("Failed to generate key via EVP_BytesToKey");
    }

    // 初始化加密上下文
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx) {
        throw std::runtime_error("Failed to create EVP_CIPHER_CTX");
    }

    if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key, iv) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        throw std::runtime_error("Failed to initialize encryption");
    }

    // 计算密文缓冲区大小
    int ciphertext_len = plaintext.length() + EVP_CIPHER_block_size(EVP_aes_256_cbc());
    unsigned char* ciphertext = new unsigned char[ciphertext_len];
    int len;

    // 执行加密
    if (EVP_EncryptUpdate(ctx, ciphertext, &len, 
                          reinterpret_cast<const unsigned char*>(plaintext.c_str()), plaintext.length()) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        delete[] ciphertext;
        throw std::runtime_error("Failed to perform encryption update");
    }
    int ciphertext_total_len = len;

    if (EVP_EncryptFinal_ex(ctx, ciphertext + len, &len) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        delete[] ciphertext;
        throw std::runtime_error("Failed to finalize encryption");
    }
    ciphertext_total_len += len;

    EVP_CIPHER_CTX_free(ctx);

    // 按CryptoJS格式打包:Salted__前缀 + 8字节盐 + 密文
    unsigned char* packed_data = new unsigned char[8 + 8 + ciphertext_total_len];
    memcpy(packed_data, "Salted__", 8);
    memcpy(packed_data + 8, salt, 8);
    memcpy(packed_data + 16, ciphertext, ciphertext_total_len);
    size_t packed_len = 16 + ciphertext_total_len;

    // Base64编码
    std::string encoded = base64_encode(packed_data, packed_len);

    // 清理内存
    delete[] ciphertext;
    delete[] packed_data;

    return encoded;
}

int main() {
    const std::string SecuritySalt = "Amidst the bustling city streets, a solitary figure found solace in the melody of raindrops dancing upon the cobblestones.sdwe2q";
    const std::string secret_key = "Whispersofthewindechoedthroughth";
    const std::string message = "1111111111";

    // 拼接盐与消息
    std::string plaintext = SecuritySalt + message;

    try {
        std::string encrypted = encrypt_aes_cryptojs_compatible(plaintext, secret_key);
        std::cout << "encrypt message: " << encrypted << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

关键说明

  • EVP_BytesToKey算法:CryptoJS默认使用该算法从字符串密码生成32位AES密钥和16位IV,参数为MD5哈希、迭代1次、随机8字节盐
  • Salted__格式:CryptoJS输出的密文必须是Salted__前缀 + 8字节盐 + 实际密文的组合,再进行Base64编码,否则无法被Node.js端解密
  • 随机盐:每次加密生成不同盐,因此相同明文的加密结果不同,但均能被正确解密

编译时需链接OpenSSL库,示例命令:

g++ -o aes_encrypt aes_encrypt.cpp -lcrypto

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 18:51:17