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

AES文件解密报错:invalid PKCS #7 block padding问题求助

解密文件时出现PKCS#7填充错误的解决方案

问题描述

我正在开发一个包含文件加解密功能的项目,处理后文件会被加密并保存为自定义扩展名.lok的文件,格式为「IV#加密文件字节」。解密时遇到报错:"invalid PKCS #7 block padding found in file decryption"。已在.txt文件上测试代码,检查过密钥、IV及加密文件字节,未发现存储导致的异常,附上相关代码,恳请协助解决。

加密函数代码

void encryptFile(const byte* AESkey, const std::string& path)
{
    
    std::ifstream binaryFile;
    std::ofstream encryptedFile;
    std::string encryptedDataBinary;

    binaryFile.open(path, std::ios::binary);
    encryptedFile.open(path + ".lok", std::ios::binary);

    // Initialize with a random IV
    CryptoPP::SecByteBlock iv(CryptoPP::AES::BLOCKSIZE); 
    CryptoPP::AutoSeededRandomPool prng;
    prng.GenerateBlock(iv, iv.size());
    

    // Write the IV as raw bytes to the beginning of the encrypted file
    encryptedFile.write(reinterpret_cast<const char*>(iv.data()), iv.size());
    encryptedFile << '#'; // Add the separator


    // Create AES encryption object
    CryptoPP::CBC_Mode< CryptoPP::AES >::Encryption e;
    e.SetKeyWithIV(AESkey, CryptoPP::AES::DEFAULT_KEYLENGTH, iv);


    // Read the entire file
    std::string inputData(
        (std::istreambuf_iterator<char>(binaryFile)),
        std::istreambuf_iterator<char>()
    );       


    // Encrypt the data
    // The StreamTransformationFilter adds padding
    //  as required. ECB and CBC Mode must be padded
    //  to the block size of the cipher.
    CryptoPP::StringSource(inputData, true, new CryptoPP::StreamTransformationFilter(e, new CryptoPP::StringSink(encryptedDataBinary)));


    encryptedFile << encryptedDataBinary;

    // Close the files
    binaryFile.close();
    encryptedFile.close();
    std::remove(path.c_str());
}

解密函数代码

void decryptFile(const byte* AESkey, std::string& path)
{
    std::string decrypted;
    CryptoPP::SecByteBlock iv(CryptoPP::AES::BLOCKSIZE);
    std::fstream decryptedFile;
    std::ifstream encryptedFile;
    std::stringstream inputData;

    decryptedFile.open(revertFileExtension(path), std::ios::binary);
    encryptedFile.open(path);

    // Read the IV from the beginning of the encrypted file
    encryptedFile.read(reinterpret_cast<char*>(iv.data()), iv.size());

    encryptedFile.rdstate();

    // Skip the "#" separator
    char separator;
    encryptedFile.read(&separator, 1);

    // Process the rest of the file (excluding the IV and separator)
    inputData << encryptedFile.rdbuf();
    
    //decoding hex data to just aes encrypted

    //decrypting aes
    CryptoPP::CBC_Mode< CryptoPP::AES >::Decryption d;
    d.SetKeyWithIV(AESkey, CryptoPP::AES::DEFAULT_KEYLENGTH, iv);


    // The StreamTransformationFilter removes
    //padding as required.
    CryptoPP::StringSource ssd(inputData.str(), true, new CryptoPP::StreamTransformationFilter(d, new CryptoPP::StringSink(decrypted)));

    decryptedFile << decrypted;

    // Close the files
    encryptedFile.close();
    decryptedFile.close();

    // Remove the original encrypted file
    std::remove(path.c_str());
}

问题分析与修复方案

核心问题

  1. 混合使用二进制/文本模式操作文件:

    • 加密时,encryptedFile以二进制模式打开,但写入分隔符#和加密数据时用了<<文本流运算符,在Windows等系统中会自动转换换行符(比如把\n转成\r\n),破坏二进制加密数据的完整性。
    • 解密时,打开加密文件未指定std::ios::binary,默认文本模式会导致读取二进制数据时丢失或篡改字节,比如将某些控制字符视为文件结束符,导致读取的加密数据不完整,最终触发PKCS#7填充验证失败。
  2. 缺少文件操作的错误检查:读取IV、分隔符等步骤未校验是否成功,若出现读取不完整(比如文件损坏),会使用错误的IV或不完整的加密数据进行解密,直接导致填充错误。

具体修复步骤

1. 统一使用二进制模式读写所有文件

  • 解密时打开加密文件需添加std::ios::binary:
    encryptedFile.open(path, std::ios::binary);
    
  • 加密时写入分隔符和加密数据改用write方法(二进制写入),避免文本模式转换:
    // 替换原 encryptedFile << '#'
    encryptedFile.write("#", 1);
    // 替换原 encryptedFile << encryptedDataBinary
    encryptedFile.write(encryptedDataBinary.data(), encryptedDataBinary.size());
    
  • 解密后写入文件也改用write方法:
    // 替换原 decryptedFile << decrypted
    decryptedFile.write(decrypted.data(), decrypted.size());
    

2. 添加文件操作的错误校验

在读取IV、分隔符后增加检查:

// 读取IV后检查
encryptedFile.read(reinterpret_cast<char*>(iv.data()), iv.size());
if (!encryptedFile) {
    throw std::runtime_error("读取IV失败");
}

// 读取分隔符后检查
char separator;
encryptedFile.read(&separator, 1);
if (!encryptedFile || separator != '#') {
    throw std::runtime_error("分隔符无效或读取失败");
}

3. 移除无用代码

解密函数中的encryptedFile.rdstate();只是获取状态但未处理,可删除该语句,改为直接检查读取操作的返回值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:59:51