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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:57:07