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是流加密模式,加密后数据长度和明文完全一致,不会损坏数据)。
具体问题点:
- 文件大小获取不准确:
_filelength返回long类型,在32位系统下最大只能表示2GB,若ZIP文件超过这个大小会导致获取的文件长度错误;另外,_sopen_s打开的文件句柄用_filelength获取大小,可能受文件缓存影响导致数值不准。 - 文件读取不完整且未校验:用
_fdopen将文件句柄转为FILE*后,调用fread读取数据,但没有检查bytes_read是否等于file_size,如果读取过程中出现错误(比如磁盘IO问题、文件被占用),会导致明文数据不完整,加密后的内容自然缺失,解密后ZIP文件结构损坏。 - 资源泄漏:打开的文件句柄
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
相关产品推荐
相关产品推荐

