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

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用无效标签校验自然失败。

解决方案

  1. 在C端加密函数中,为authInfo配置pbTag(指向密文后的缓冲区)和cbTag(设置为16字节,GCM标准标签长度)。
  2. 加密后输出的缓冲区包含密文(长度=明文长度)+认证标签(16字节),Python端按此拆分即可正常解密。

修改后的C代码会正确生成有效的认证标签,Python端使用对应标签即可通过MAC校验,成功解密出明文。

内容的提问来源于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 08:15:56