C(bcrypt.h)实现AES-256 GCM加密后Python解密MAC校验失败求助
AES-GCM跨语言加密解密MAC校验失败问题
问题背景
使用Windows BCrypt库(C语言)实现AES-GCM加密字符串"Hello, world!",加密过程无报错,但用Python的PyCryptodome库解密时触发ValueError: MAC check failed。已确认Python使用的密钥、IV、密文、认证标签均来自C端输出(标签取加密数据最后16字节,前14字节为密文),替换固定IV/密钥后问题依旧,怀疑认证标签处理逻辑错误。
C端加密代码
#include <windows.h> #include <bcrypt.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #pragma comment(lib, "bcrypt.lib") 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) { 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 = encryptedData + plainDataLength; authInfo.cbTag = 16; 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 main() { char *input = "Hello, world!"; BYTE key[32]; BYTE iv[12]; BYTE *encrypted; ULONG encryptedSize = 14 + 16; // 明文14字节 + GCM标签16字节 // 生成密钥和IV NTSTATUS status = generateRandomBytes(key, sizeof(key)); if (!NT_SUCCESS(status)) { printf("Failed to generate key: 0x%x\n", status); return 1; } status = generateRandomBytes(iv, sizeof(iv)); if (!NT_SUCCESS(status)) { printf("Failed to generate IV: 0x%x\n", status); return 1; } // 打印密钥 printf("Key:\n"); for (int i = 0; i < sizeof(key); i++) { printf("%02X", key[i]); } printf("\n"); // 打印IV printf("IV:\n"); for (int i = 0; i < sizeof(iv); i++) { printf("%02X", iv[i]); } printf("\n"); encrypted = (BYTE *)malloc(encryptedSize); if (!encrypted) { printf("Memory allocation failed\n"); return 1; } // 执行加密并检查状态 status = encrypt_AES_GCM((BYTE*)input, 14, iv, 12, key, 32, encrypted, encryptedSize); if (!NT_SUCCESS(status)) { printf("Encryption failed: 0x%x\n", status); free(encrypted); return 1; } // 打印加密后的数据(密文+标签) printf("Encrypted bytes (ciphertext + tag):\n"); for (ULONG i = 0; i < encryptedSize; i++) { printf("%02X", encrypted[i]); } printf("\n"); free(encrypted); return 0; }
Python端解密代码
from Crypto.Cipher import AES # 替换为C端输出的实际数据 key = bytes.fromhex('14D63CB4BDE1A3D164DB09E44B6C877613CB7C0E281148653BE3EDFC9D476C73') iv = bytes.fromhex('27FEF35218E2FC4391971C5F') # 密文取加密数据前14字节,标签取后16字节 encrypted_hex = 'A0660CFE8F01000050010CFE8F0100003A5C50726F6772616D2046696C65' ciphertext = bytes.fromhex(encrypted_hex[:28]) # 14字节=28个十六进制字符 authTag = bytes.fromhex(encrypted_hex[28:]) # 16字节=32个十六进制字符 cipher = AES.new(key, AES.MODE_GCM, nonce=iv) plaintext = cipher.decrypt_and_verify(ciphertext, authTag) print("Decrypted plaintext:", plaintext.decode('utf-8'))
示例输出与错误
C端原错误输出
Key: 14D63CB4BDE1A3D164DB09E44B6C877613CB7C0E281148653BE3EDFC9D476C73 IV: 27FEF35218E2FC4391971C5F Encrypted bytes: A0660CFE8F01000050010CFE8F0100003A5C50726F6772616D2046696C65
Python端原错误
Traceback (most recent call last): File "C:\Users\User\Documents\Projects\Testing code\test.py", line 64, in <module> plaintext = cipher.decrypt_and_verify(ciphertext, authTag) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\Crypto\Cipher\_mode_gcm.py", line 567, in decrypt_and_verify self.verify(received_mac_tag) File "C:\Users\User\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\Crypto\Cipher\_mode_gcm.py", line 508, in verify raise ValueError("MAC check failed") ValueError: MAC check failed
问题根源与解决方案
问题根源
原C代码未配置BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO的pbTag和cbTag字段,导致BCrypt不会将GCM认证标签写入输出缓冲区,输出的"标签部分"实际是内存垃圾数据,Python用无效标签校验自然失败。
解决方案
- 在C端加密函数中,为
authInfo配置pbTag(指向密文后的缓冲区)和cbTag(设置为16字节,GCM标准标签长度)。 - 加密后输出的缓冲区包含密文(长度=明文长度)+认证标签(16字节),Python端按此拆分即可正常解密。
修改后的C代码会正确生成有效的认证标签,Python端使用对应标签即可通过MAC校验,成功解密出明文。
内容的提问来源于stack exchange,提问作者exomm
相关产品推荐
相关产品推荐

