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

BCrypt AES-256 GCM加密ZIP文件后解密损坏问题排查

问题:AES-256 GCM加密ZIP文件后解密损坏

我用C语言通过BCrypt实现AES-256 GCM加密ZIP文件,再用Python解密。小文件(比如仅含“Hello, world!”的txt)加密解密正常,但加密包含test1.zip、test2.zip和test.txt的ZIP文件后,解密结果里test.txt消失,test2.zip损坏无法打开,只有test1.zip正常。请问是算法导致损坏,还是代码存在问题?

C语言加密代码

NTSTATUS generateRandomBytes(BYTE *buffer, ULONG length)
{
    BCRYPT_ALG_HANDLE hProvider;
    NTSTATUS status = BCryptOpenAlgorithmProvider(&hProvider, BCRYPT_RNG_ALGORITHM, NULL, 0);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    status = BCryptGenRandom(hProvider, buffer, length, 0);
    BCryptCloseAlgorithmProvider(hProvider, 0);
    return status;
}

NTSTATUS encrypt_AES_GCM(const BYTE *plainData, ULONG plainDataLength,
                         const BYTE *iv, ULONG ivLength,
                         const BYTE *key, ULONG keyLength,
                         BYTE *encryptedData, ULONG encryptedDataLength,
                         BYTE *authTag, ULONG authTagLength)
{
    NTSTATUS status = 0;
    DWORD bytesDone = 0;
    BCRYPT_ALG_HANDLE algHandle = 0;
    status = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_AES_ALGORITHM, NULL, 0);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    status = BCryptSetProperty(algHandle, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
    if (!NT_SUCCESS(status))
    {
        BCryptCloseAlgorithmProvider(algHandle, 0);
        return status;
    }

    BCRYPT_KEY_HANDLE keyHandle = 0;
    status = BCryptGenerateSymmetricKey(algHandle, &keyHandle, NULL, 0, (PUCHAR)key, keyLength, 0);
    if (!NT_SUCCESS(status))
    {
        BCryptCloseAlgorithmProvider(algHandle, 0);
        return status;
    }

    BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
    BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
    authInfo.pbNonce = (PUCHAR)iv;
    authInfo.cbNonce = ivLength;
    authInfo.pbTag = authTag;
    authInfo.cbTag = authTagLength;

    status = BCryptEncrypt(keyHandle, (PUCHAR)plainData, plainDataLength, &authInfo, NULL, 0, encryptedData, encryptedDataLength, &bytesDone, 0);
    if (!NT_SUCCESS(status))
    {
        BCryptDestroyKey(keyHandle);
        BCryptCloseAlgorithmProvider(algHandle, 0);
        return status;
    }

    BCryptDestroyKey(keyHandle);
    BCryptCloseAlgorithmProvider(algHandle, 0);

    return status;
}

int GCM_Encrypt_File(char *FileToEncrypt, char *keys, char *OutputFile)
{
    int fd;
    long file_size;
    char *input;
    _sopen_s(&fd, FileToEncrypt, _O_RDONLY, _SH_DENYRW, _S_IREAD);
    file_size = _filelength(fd);
    input = (char *)malloc(file_size);
    size_t bytes_read = fread(input, 1, file_size, _fdopen(fd, "rb"));
    BYTE key[32];
    BYTE iv[12];
    BYTE KeyTagIV[60];
    BYTE *encrypted;
    ULONG encryptedSize = file_size;
    ULONG authTagLength = 16;
    BYTE authTag[16];

    // Generate key and iv
    generateRandomBytes(key, sizeof(key));
    generateRandomBytes(iv, sizeof(iv));
    // Concatenate key and IV
    memcpy(KeyTagIV, key, sizeof(key));
    memcpy(KeyTagIV + sizeof(key), iv, sizeof(iv));
    // Print the info

    encrypted = (BYTE *)malloc(encryptedSize);

    // Encrypt
    encrypt_AES_GCM((BYTE *)input, file_size, iv, 12, key, 32, encrypted, encryptedSize, authTag, authTagLength);
    
    // Print data
    printf("KEY:\n");
    for (ULONG i = 0; i < sizeof(key); i++)
    {
        printf("%02X", key[i]);
    }
    printf("\n");
    printf("IV:\n");
    for (ULONG i = 0; i < sizeof(iv); i++)
    {
        printf("%02X", iv[i]);
    }
    printf("\n");
    printf("Authentication tag:\n");
    for (ULONG i = 0; i < authTagLength; i++)
    {
        printf("%02X", authTag[i]);
    }
    printf("\n");

    // Add authentication tag to the KeyTagIV variable 
    memcpy(KeyTagIV + 44, authTag, authTagLength);
    memcpy(keys, KeyTagIV, 60);
    printf("FULL:\n");
    for (ULONG i = 0; i < 60; i++)
    {
        printf("%02X", KeyTagIV[i]);
    }
    printf("\n");
    FILE *encryptedFile = fopen(OutputFile, "wb");
    fwrite(encrypted, sizeof(char), file_size, encryptedFile);
    free(encrypted);
    return 1;
}

int main()
{
        // GCM Encrypt the File.
        char key_data[60];
        char path[] = "test.txt";
        char path1[] = "test.enc";
        GCM_Encrypt_File(path, key_data, path1);
        printf("Decryption Data:");
        for (int i = 0; i < 60; i++) {
            printf("%02X ", key_data[i]);
        }

    return 0;
}

Python解密代码

import rsa
from Crypto.Cipher import AES
import base64
import os
if __name__ == '__main__':
    # Decryption data in HEX KEY; IV; TAG
    KEYS = """540AA548ADBBF19820FEC1DDF9BC19B6230A746C0CF0EA87E083FDF314867DA525F299D2B9FEBC26A864A9F149D3A60B05E03CA9C3328E5AB10228DB"""
    encrypted_keys = bytearray.fromhex(KEYS)

    ENC_PATH = "test.enc"
    keys_data = encrypted_keys

    key = keys_data[:32]
    iv = keys_data[32:44]
    authTag = keys_data[44:60]
    ciphertext = open(ENC_PATH, mode="rb").read()
    cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
    plaintext = cipher.decrypt_and_verify(ciphertext, authTag)
    with open('decrypted_test.txt', 'wb') as f:
        f.write(plaintext)
    print("Done.")

#decrypt_rsa(input_filename, output_filename)

问题分析与修复

问题出在C代码的文件读取逻辑,和AES-GCM算法本身无关(GCM是流加密模式,加密后数据长度和明文完全一致,不会损坏数据)。

具体问题点:

  1. 文件大小获取不准确:_filelength返回long类型,在32位系统下最大只能表示2GB,若ZIP文件超过这个大小会导致获取的文件长度错误;另外,_sopen_s打开的文件句柄用_filelength获取大小,可能受文件缓存影响导致数值不准。
  2. 文件读取不完整且未校验:用_fdopen将文件句柄转为FILE*后,调用fread读取数据,但没有检查bytes_read是否等于file_size,如果读取过程中出现错误(比如磁盘IO问题、文件被占用),会导致明文数据不完整,加密后的内容自然缺失,解密后ZIP文件结构损坏。
  3. 资源泄漏:打开的文件句柄fd没有关闭,可能导致系统资源耗尽,但这不是直接导致文件损坏的原因。

修复后的文件读取逻辑:

替换GCM_Encrypt_File中的文件读取部分为以下代码,确保完整读取文件内容:

int GCM_Encrypt_File(char *FileToEncrypt, char *keys, char *OutputFile)
{
    FILE *inputFile = fopen(FileToEncrypt, "rb");
    if (!inputFile)
    {
        printf("Failed to open input file\n");
        return -1;
    }

    // 获取文件大小(支持大文件)
    fseek(inputFile, 0, SEEK_END);
    long long file_size = ftell(inputFile);
    if (file_size == -1)
    {
        printf("Failed to get file size\n");
        fclose(inputFile);
        return -1;
    }
    fseek(inputFile, 0, SEEK_SET);

    // 分配内存并读取文件
    char *input = (char *)malloc(file_size);
    if (!input)
    {
        printf("Failed to allocate memory\n");
        fclose(inputFile);
        return -1;
    }

    size_t bytes_read = fread(input, 1, file_size, inputFile);
    if (bytes_read != file_size)
    {
        printf("Failed to read full file, read %zu bytes out of %lld\n", bytes_read, file_size);
        free(input);
        fclose(inputFile);
        return -1;
    }
    fclose(inputFile);

    // 后续加密逻辑不变...
}

额外注意事项:

  • 如果需要支持超大文件(超过4GB),建议改用_fseeki64和_ftelli64来获取文件大小。
  • 加密完成后,要确保encrypted内存被正确释放,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 02:19:56