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
相关产品推荐
相关产品推荐

