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

基于AES-GCM的文件分块加密实现正确性及MAC计算问询

AES-GCM分块加密文件的正确性问题及解决方案

问题描述

我希望编写一个生成器,将文件按指定大小分块加密并逐个返回加密块;由于需要对载荷进行认证,因此选择了AES-GCM算法。

选择分块加密而非一次性加密整个文件的原因是:我需要将这些块通过网络传输,若先加密整个(可能很大的)文件并存储,传输时再分块会冗余,因此直接加密待传输的单个块并立即发送。

但我不确定当前实现是否正确,且未找到对应场景的实用文档,以下是我的代码:

from Crypto.Cipher import AES
from Crypto import Random
import struct

CHUNK_SIZE = 32
AES_BLOCK_SIZE = 16

def encryptFileInChunks(key: bytes, infile: str, file_size: int):
    iv = Random.new.read(16)
    encryptor = AES.new(key, AES.MODE_GCM, iv, mac_len=16)

    with open(infile, 'r') as infile:
        # write the original file size as little endian 8 byte int
        # so that we can remove the padding after decryption by
        # truncating to this original length
        yield struct.pack("<Q", file_size)
        # iv for decryption
        yield iv

    while True:
        chunk = infile.read(CHUNK_SIZE)
        if len(chunk) == 0:
            break
        if len(chunk) % AES_BLOCK_SIZE != 0:
            # add padding to make a valid block
            chunk += b" " * (AES_BLOCK_SIZE - len(chunk) % AES_BLOCK_SIZE)
        
        c, d = encryptor.encrypt_and_digest(chunk)

        # only return ciphertext
        yield c, None
    
    # first tuple value being None indicates end of ciphertext,
    # return the mac
    yield None, d

请问该实现是否能按照GCM模式的要求正确计算整个载荷的MAC,还是仅返回最后一个块的MAC?如何以类似方式正常计算整个密文的MAC,而无需为每个块单独发送(密文, MAC)对?

我考虑过使用AES-CBC搭配HMAC单独认证,但这会增加少许开销,而AES-GCM的速度更快。


问题分析与解决方案

当前实现的核心错误

  1. 文件操作缩进错误:while循环位于with open块外部,此时文件已经被关闭,无法读取任何内容,直接导致加密逻辑无法执行。
  2. GCM模式使用错误:
    • GCM是流加密模式,不需要对明文进行填充,它原生支持任意长度的输入。你添加的填充不仅多余,还会导致解密后需要额外处理去掉填充,增加复杂度。
    • encrypt_and_digest()方法会一次性处理输入并生成对应的MAC,且每次调用都会终止当前加密上下文。你对每个块调用该方法,得到的是单个块的MAC,而非整个文件的全局MAC,完全不符合GCM的认证要求。

正确实现

以下是修正后的代码,它会分块加密文件,最终生成整个文件的全局MAC,同时保持逐块输出密文的特性:

from Crypto.Cipher import AES
from Crypto import Random
import struct

CHUNK_SIZE = 32

def encryptFileInChunks(key: bytes, infile_path: str, file_size: int):
    # GCM推荐使用12字节IV(96位),兼顾性能与安全性,16字节IV也兼容
    iv = Random.new().read(12)
    encryptor = AES.new(key, AES.MODE_GCM, iv, mac_len=16)

    with open(infile_path, 'rb') as infile:
        # 先发送原始文件大小(用于解密后截断)和IV
        yield struct.pack("<Q", file_size)
        yield iv

        # 逐块读取并加密
        while True:
            chunk = infile.read(CHUNK_SIZE)
            if not chunk:
                break
            # 分块加密,累积MAC计算上下文但不生成最终MAC
            ciphertext_chunk = encryptor.encrypt(chunk)
            yield ciphertext_chunk, None

        # 所有块处理完成后,生成全局MAC
        mac = encryptor.digest()
        yield None, mac

关键说明

  1. 文件读取:将while循环放入with块内部,确保文件在读取过程中保持打开状态;同时使用'rb'模式读取文件,避免文本模式的编码问题。
  2. GCM的正确用法:
    • 使用encrypt()方法分块处理明文,该方法会累积计算MAC所需的上下文数据,但不会生成最终MAC。
    • 所有明文块处理完成后,调用digest()生成整个文件的全局MAC,这个MAC是基于所有明文块、IV和密钥计算得出的,完全符合GCM的认证要求。
  3. IV选择:GCM推荐使用12字节IV,能让实现更高效且避免某些安全隐患,16字节IV也合法但性能略低。
  4. 无需填充:GCM对任意长度的明文都能处理,不需要额外添加填充,解密时直接根据原始文件大小截断即可。

解密端对应逻辑(补充参考)

解密时需要先接收文件大小和IV,然后逐块接收密文并解密,最后接收MAC并验证:

def decryptFileInChunks(key: bytes, outfile_path: str):
    # 此处模拟网络接收逻辑,实际场景需替换为真实的数据流读取
    def receive_data():
        # 示例:从网络或其他来源获取加密后的数据块
        pass

    data_iter = receive_data()
    # 接收文件大小和IV
    file_size_bytes = next(data_iter)
    file_size = struct.unpack("<Q", file_size_bytes)[0]
    iv = next(data_iter)

    decryptor = AES.new(key, AES.MODE_GCM, iv, mac_len=16)

    with open(outfile_path, 'wb') as outfile:
        while True:
            chunk_data = next(data_iter)
            if chunk_data[0] is None:
                # 接收MAC并验证完整性
                mac = chunk_data[1]
                try:
                    decryptor.verify(mac)
                except ValueError:
                    raise Exception("MAC验证失败,数据可能被篡改或损坏")
                break
            # 解密块并写入文件
            plaintext_chunk = decryptor.decrypt(chunk_data[0])
            outfile.write(plaintext_chunk)

        # 截断到原始文件大小,确保与原文件一致
        outfile.truncate(file_size)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 16:59:51