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

分段传输加密文件:非首段开头数据损坏问题求助

大文件AES加密分段传输后后续数据块开头损坏问题

C客户端加密文件后发送至Python服务器解密保存,小文件(单次读取1024字节)传输正常,但大文件分段传输时,除第一个数据块外,后续每个数据块开头部分损坏。经排查确认,损坏数据在C端加密第一个循环时产生。

C++端加密及发送代码

//Encrypt the data and save it to temporary file
std::ofstream tempFile("EncryptedFile", std::ios::binary);  
char buffer[MAX_LEN];
std::cout << "The data inside buffer is:\n";
while (!transferFile.eof()) {
    std::fill_n(buffer, sizeof(buffer), '\0');
    transferFile.read(buffer, sizeof(buffer)-1);
    std::streamsize bytesRead = transferFile.gcount();
    std::cout << buffer;
    if (bytesRead > 0) {
        string encryptedChunk = aesWrapper.encrypt(buffer, bytesRead);
        tempFile.write(encryptedChunk.c_str(), encryptedChunk.size());
    }
}
transferFile.close();
tempFile.close();

//Step 4: get the file size and send  it
std::ifstream tempFileRead("EncryptedFile", std::ios::binary | std::ios::ate);
std::streamsize fileSize = tempFileRead.tellg();
tempFileRead.seekg(0, std::ios::beg);
string header = createHeader(std::to_string(SEND_FILE));
header += ("\nPayload size: " + std::to_string(fileSize + fileName.length()) + "\nContent Size: ") + std::to_string(fileSize) + "\nFile Name: " + fileName;
std::string utf8_request(header);
boost::asio::write(s, boost::asio::buffer(utf8_request.c_str(), utf8_request.length()));
while (tempFileRead) {
    tempFileRead.read(buffer, sizeof(buffer)-1);
    std::streamsize bytesRead = tempFileRead.gcount();
    if (bytesRead > 0) {
        boost::asio::write(s, boost::asio::buffer(buffer, bytesRead));
    }
}
tempFileRead.close();
std::remove("EncryptedFile");

Python端接收解密代码

iv = b"\x00" * 16  # just for practical use should not be set to 0
UUID = getUUID(request)
AES_Key = DB.getAESKey(UUID)
fileName = getFileName(request)
ContentSize = int(getContentSize(request))
cipher = AES.new(AES_Key, AES.MODE_CBC, iv)
decrypted_bytes = b''

while ContentSize > 0:
    data = server.getBinaryData()
    ContentSize -= len(data)
    decrypted_bytes += cipher.decrypt(data)

decrypted_text = unpad(decrypted_bytes, AES.block_size)

with open(fileName, 'wb') as file:
    file.write(decrypted_text)

问题根源及修复方案

核心问题

AES-CBC模式是链式加密,第一个块用初始IV加密,后续每个块的IV必须是前一个块的密文。原C++代码中,aesWrapper.encrypt若每次调用都重新初始化加密上下文(重置IV),会导致后续所有块用错误IV加密,最终解密时后续块开头出现损坏。

此外,文件读取循环条件while (!transferFile.eof())存在逻辑缺陷,会导致最后一次读取失败后仍执行加密操作,可能引入无效数据。

修复步骤

  1. 维护AES加密上下文状态
    初始化一次加密器,分块加密时持续更新IV,而非每次加密都重新初始化:

    // 初始化加密器(仅执行一次)
    aesWrapper.initEncrypt();
    std::ofstream tempFile("EncryptedFile", std::ios::binary);  
    char buffer[MAX_LEN];
    
    // 读取文件并分块加密
    while (transferFile.read(buffer, sizeof(buffer))) {
        string encryptedChunk = aesWrapper.updateEncrypt(buffer, sizeof(buffer));
        tempFile.write(encryptedChunk.c_str(), encryptedChunk.size());
    }
    // 处理剩余数据及最终填充
    std::streamsize bytesRead = transferFile.gcount();
    if (bytesRead > 0) {
        string encryptedChunk = aesWrapper.updateEncrypt(buffer, bytesRead);
        tempFile.write(encryptedChunk.c_str(), encryptedChunk.size());
    }
    string finalChunk = aesWrapper.finalEncrypt();
    if (!finalChunk.empty()) {
        tempFile.write(finalChunk.c_str(), finalChunk.size());
    }
    transferFile.close();
    tempFile.close();
    
  2. 修正文件读取循环逻辑
    替换原eof()判断,改为先读取再判断实际读取字节数,避免无效循环:

    // 替换发送加密文件的循环
    while (tempFileRead.read(buffer, sizeof(buffer))) {
        boost::asio::write(s, boost::asio::buffer(buffer, sizeof(buffer)));
    }
    std::streamsize bytesRead = tempFileRead.gcount();
    if (bytesRead > 0) {
        boost::asio::write(s, boost::asio::buffer(buffer, bytesRead));
    }
    
  3. 优化二进制数据读取
    加密后的文件是二进制格式,无需预留sizeof(buffer)-1的空间,直接读取整个缓冲区即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 07:34:53