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

C#用EasyCrypto加密AES后,C++用Crypto++解密报错排查

问题分析与修复方案

你遇到的"ciphertext length is not a multiple of block size"错误,根源在于代码里存在多个关键逻辑错误,以下是具体问题和修复步骤:

1. 密钥生成逻辑错误

EasyCrypto并非直接将密码作为AES密钥,而是通过PBKDF2算法(基于Rfc2898DeriveBytes),结合加密数据中嵌入的盐值生成32字节的AES密钥。你的代码直接把密码转成byte数组当密钥,完全不符合EasyCrypto的密钥生成规则。

2. IV提取索引越界

原代码中提取IV时,循环索引错误导致数组越界:

// 错误写法
for (int i = 8; i < 24; i++) {
    iv[i] = pkByteArray[i];
}

iv数组只有16个元素(索引0-15),但i从8到23,会超出iv的范围。正确的应该是将pkByteArray[8-23]对应赋值到iv[0-15]:

// 正确写法
for (int i = 8; i < 24; i++) {
    iv[i - 8] = pkByteArray[i];
}

3. 未正确提取加密数据

你在解密时传入的是固定字符串"encoded string",而不是从加密数据中提取的真正密文部分。根据EasyCrypto的格式:

  • 头部分总计127字节
  • 之后是附加头数据(版本1时长度为0)
  • 最后才是加密数据

需要先读取附加头数据长度(偏移123的4字节,小端字节序),然后计算加密数据的起始位置,再截取这部分作为解密的输入。

4. 完整修复后的代码

以下是修正后的C++解密代码:

#include <CryptoPP/aes.h>
#include <CryptoPP/modes.h>
#include <CryptoPP/filters.h>
#include <CryptoPP/hex.h>
#include <CryptoPP/pwdbased.h>
#include <CryptoPP/sha.h>
#include <iostream>
#include <string>

using namespace CryptoPP;

// 从密码和盐生成PBKDF2密钥
void GeneratePBKDF2Key(const byte* password, size_t passwordLen, const byte* salt, size_t saltLen, byte* outKey, size_t keyLen)
{
    // EasyCrypto默认使用SHA256、100000次迭代
    PKCS5_PBKDF2_HMAC<SHA256> pbkdf2;
    pbkdf2.DeriveKey(outKey, keyLen, 0, password, passwordLen, salt, saltLen, 100000);
}

string CBCMode_Decrypt(const byte key[], int keySize, const byte iv[], const byte* ciphertext, size_t ciphertextLen) {
    string recovered = "";
    try
    {
        CBC_Mode<AES>::Decryption d;
        d.SetKeyWithIV(key, keySize, iv);
        
        // 使用提取的真实密文进行解密
        StringSource s(ciphertext, ciphertextLen, true,
            new StreamTransformationFilter(d, new StringSink(recovered)));
    }
    catch (const Exception& e)
    {
        cerr << e.what() << endl;
        exit(1);
    }
    return recovered;
}

string Decrypt(const string& hexPassword, const string& hexEncryptedData) {
    // 解码密码为原始字节
    string passwordBytes;
    StringSource ss(hexPassword, true, new HexDecoder(new StringSink(passwordBytes)));
    
    // 解码加密数据为原始字节
    string encryptedRaw;
    StringSource ss2(hexEncryptedData, true, new HexDecoder(new StringSink(encryptedRaw)));
    const byte* pkByteArray = reinterpret_cast<const byte*>(encryptedRaw.data());
    size_t rawLen = encryptedRaw.size();

    // 1. 提取IV(偏移8-23,共16字节)
    byte iv[16];
    for (int i = 8; i < 24; ++i) {
        iv[i - 8] = pkByteArray[i];
    }

    // 2. 提取盐值(偏移24-55,共32字节)
    byte salt[32];
    for (int i = 24; i < 56; ++i) {
        salt[i - 24] = pkByteArray[i];
    }

    // 3. 生成AES密钥(32字节)
    byte key[32];
    GeneratePBKDF2Key(reinterpret_cast<const byte*>(passwordBytes.data()), passwordBytes.size(),
                      salt, sizeof(salt), key, sizeof(key));

    // 4. 读取附加头数据长度(偏移123的4字节,小端字节序)
    uint32_t additionalHeaderLen = *reinterpret_cast<const uint32_t*>(pkByteArray + 123);
    // 加密数据起始位置:127字节头 + 附加头长度
    size_t cipherStart = 127 + additionalHeaderLen;
    if (cipherStart >= rawLen) {
        cerr << "Invalid encrypted data format" << endl;
        exit(1);
    }
    const byte* ciphertext = pkByteArray + cipherStart;
    size_t ciphertextLen = rawLen - cipherStart;

    // 5. 执行解密
    return CBCMode_Decrypt(key, sizeof(key), iv, ciphertext, ciphertextLen);
}

// 示例调用
int main() {
    string hexPassword = "你的密码的十六进制字符串";
    string hexEncryptedData = "EasyCrypto返回的加密字符串的十六进制形式";
    string plaintext = Decrypt(hexPassword, hexEncryptedData);
    cout << "解密结果:" << plaintext << endl;
    return 0;
}

额外说明

  • 代码中使用的PBKDF2参数(SHA256、100000次迭代)与EasyCrypto的默认配置一致,若你修改过EasyCrypto的迭代次数或哈希算法,需同步调整此处。
  • 原代码未处理MAC验证,生产环境中建议添加MAC校验步骤,避免篡改的密文被解密。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 21:40:50