Crypto++文本文件解密无输出问题排查求助
问题根源分析
1. 加密代码的文件写入冲突
加密时先通过ofstream写入salt和IV到输出文件,但随后创建StreamTransformationFilter并传入FileSink(outputFileName),此时FileSink会以截断模式打开文件,覆盖之前写入的salt和IV。尽管你的测试输出显示解密读取到了正确的salt和IV,这只是巧合,实际场景下会导致加密文件丢失关键的salt/IV信息。
2. 解密代码的密文读取逻辑错误
解密时计算密文长度的方式完全错误:
inputFile.seekg(salt.size() + iv.size()); size_t fileSize = inputFile.tellg(); // 这里得到的是salt+IV的偏移量(32),而非密文长度 inputFile.seekg(0); vector<char> fileBuffer(fileSize); inputFile.read(fileBuffer.data(), fileSize); // 仅读取前32字节的salt和IV,未读取密文
你把salt和IV当成密文传入解密过滤器,自然无法解密出有效数据,导致过滤器返回0长度。
3. 解密代码的过滤器使用错误
解密时同时做了冗余操作:既让过滤器直接写入输出文件,又试图从过滤器中读取数据再写入。StreamTransformationFilter连接FileSink时,数据会直接写入文件,调用MessageEnd()后数据已输出,此时MaxRetrievable()返回0是正常的,后续的Detach和Get操作完全多余且混乱。
修复后的代码
加密代码修复
bool Cipher::encryptFile(const char* inputFileName, const char* outputFileName) { salt = generateRandomSalt(); generateRandomIV(); if (!deriveKeyFromPassword(storedPassword, strlen(storedPassword), salt)) { cerr << "Key derivation failed" << endl; return false; } if (iv.empty() || key.empty()) { cerr << "Failed to generate IV or key" << endl; return false; } try { ofstream outputFile(outputFileName, ios::binary | ios::trunc); ifstream inputFile(inputFileName, ios::binary); if (!inputFile.is_open() || !outputFile.is_open()) { cerr << "Error opening files" << endl; return false; } // 写入salt和IV到输出文件 outputFile.write(reinterpret_cast<const char*>(salt.data()), salt.size()); outputFile.write(reinterpret_cast<const char*>(iv.data()), iv.size()); cerr << "Generated salt: "; StringSource(reinterpret_cast<const byte*>(salt.data()), salt.size(), true, new HexEncoder(new FileSink(cerr))); cerr << endl; cerr << "Generated IV: "; StringSource(reinterpret_cast<const byte*>(iv.data()), iv.size(), true, new HexEncoder(new FileSink(cerr))); cerr << endl; CFB_Mode<AES>::Encryption encryption(key, key.size(), iv); // 复用已打开的outputFile,避免截断覆盖 StreamTransformationFilter filter(encryption, new FileSink(outputFile)); // 获取正确的输入文件大小 inputFile.seekg(0, ios::end); size_t fileSize = inputFile.tellg(); inputFile.seekg(0); vector<char> fileBuffer(fileSize); inputFile.read(fileBuffer.data(), fileSize); filter.Put(reinterpret_cast<const byte*>(fileBuffer.data()), fileSize); filter.MessageEnd(); inputFile.close(); outputFile.close(); cerr << "Encryption successful" << endl; return true; } catch (const ifstream::failure& e) { cerr << "File I/O error: " << e.what() << endl; return false; } catch (const Exception& e) { cerr << "Encryption error: " << e.what() << endl; return false; } }
解密代码修复
bool Cipher::decryptFile(const char* inputFileName, const char* outputFileName) { try { ifstream inputFile(inputFileName, ios::binary); ofstream outputFile(outputFileName, ios::binary | ios::trunc); if (!inputFile.is_open() || !outputFile.is_open()) { cerr << "Error opening files" << endl; return false; } // 读取salt和IV salt.resize(16); iv.resize(16); inputFile.read(reinterpret_cast<char*>(salt.data()), salt.size()); inputFile.read(reinterpret_cast<char*>(iv.data()), iv.size()); cerr << "Read salt: "; StringSource(reinterpret_cast<const byte*>(salt.data()), salt.size(), true, new HexEncoder(new FileSink(cerr))); cerr << endl; cerr << "Read IV: "; StringSource(reinterpret_cast<const byte*>(iv.data()), iv.size(), true, new HexEncoder(new FileSink(cerr))); cerr << endl; if (!deriveKeyFromPassword(storedPassword, strlen(storedPassword), salt)) { cerr << "Key derivation failed" << endl; return false; } CFB_Mode<AES>::Decryption decryption(key, key.size(), iv); // 直接让过滤器写入输出文件 StreamTransformationFilter filter(decryption, new FileSink(outputFile)); // 计算密文长度:总文件长度减去salt和IV的长度 inputFile.seekg(0, ios::end); size_t totalFileSize = inputFile.tellg(); size_t ciphertextSize = totalFileSize - salt.size() - iv.size(); inputFile.seekg(salt.size() + iv.size()); vector<char> ciphertextBuffer(ciphertextSize); inputFile.read(ciphertextBuffer.data(), ciphertextSize); cerr << "Ciphertext size: " << ciphertextSize << endl; // 仅传入密文部分到过滤器 filter.Put(reinterpret_cast<const byte*>(ciphertextBuffer.data()), ciphertextSize); filter.MessageEnd(); inputFile.close(); outputFile.close(); cerr << "Decryption successful" << endl; return true; } catch(const ifstream::failure& e) { cerr << "File I/O error: " << e.what() << endl; return false; } catch(const Exception& e) { cerr << "Decryption error: " << e.what() << endl; return false; } }
额外优化建议
generateRandomSalt函数无需返回值,直接修改成员变量salt即可,可改为void类型。- 用
std::string存储密码,替代手动管理的char数组,避免内存泄漏风险。 - 增加文件读取完整性检查(比如对比
inputFile.gcount()和预期读取长度),避免部分读取引发的错误。
内容的提问来源于stack exchange,提问作者Luca Alfino
相关产品推荐
相关产品推荐

