如何在Windows下用C语言实现无外部库的AES-256 GCM解密?
Windows下无外部依赖实现AES-256 GCM解密(C语言)
核心方案:使用系统预装的BCrypt API
Windows原生的bcrypt.h属于系统预装组件,无需额外安装第三方库,完全满足你的需求。
解决你遇到的问题
1. 初始错误(STATUS_INVALID_PARAMETER)
你最初碰到的0xC000000D错误,根源通常是两个点:
- 未正确分离认证标签:GCM模式下标签默认16字节,需从加密输出的末尾单独提取,不能和密文混在一起传入解密函数
- 结构体初始化错误:
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO必须用BCRYPT_INIT_AUTH_MODE_INFO宏初始化,不能直接用memset,否则会遗漏关键的cbSize字段设置
2. 输出长度过长、多余字节问题
这个问题是因为未正确获取并设置明文的实际有效长度。以下是修正后的完整示例代码,可解决上述所有问题:
#include <windows.h> #include <bcrypt.h> #include <stdio.h> #include <string.h> #pragma comment(lib, "bcrypt.lib") #define AES_256_KEY_SIZE 32 #define GCM_IV_SIZE 12 #define GCM_TAG_SIZE 16 NTSTATUS AES256GCM_Decrypt( const BYTE* pKey, const BYTE* pIV, const BYTE* pCiphertext, DWORD cbCiphertext, const BYTE* pTag, const BYTE* pAdditionalData, DWORD cbAdditionalData, BYTE* pPlaintext, DWORD* pcbPlaintext ) { NTSTATUS status; BCRYPT_ALG_HANDLE hAlg = NULL; BCRYPT_KEY_HANDLE hKey = NULL; DWORD cbResult = 0; // 打开AES算法句柄 status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0); if (!BCRYPT_SUCCESS(status)) goto Cleanup; // 设置算法为GCM模式 status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (BYTE*)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0); if (!BCRYPT_SUCCESS(status)) goto Cleanup; // 导入256位AES密钥 status = BCryptGenerateSymmetricKey(hAlg, &hKey, NULL, 0, (BYTE*)pKey, AES_256_KEY_SIZE, 0); if (!BCRYPT_SUCCESS(status)) goto Cleanup; // 初始化认证模式结构体(必须用官方宏) BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; BCRYPT_INIT_AUTH_MODE_INFO(authInfo); authInfo.pbNonce = (BYTE*)pIV; authInfo.cbNonce = GCM_IV_SIZE; authInfo.pbTag = (BYTE*)pTag; authInfo.cbTag = GCM_TAG_SIZE; authInfo.pbAuthData = (BYTE*)pAdditionalData; authInfo.cbAuthData = cbAdditionalData; // 先调用一次获取所需的明文缓冲区大小 status = BCryptDecrypt(hKey, (BYTE*)pCiphertext, cbCiphertext, &authInfo, NULL, 0, NULL, 0, pcbPlaintext, BCRYPT_BLOCK_PADDING); if (!BCRYPT_SUCCESS(status)) goto Cleanup; // 执行实际解密操作 status = BCryptDecrypt(hKey, (BYTE*)pCiphertext, cbCiphertext, &authInfo, NULL, 0, pPlaintext, *pcbPlaintext, &cbResult, BCRYPT_BLOCK_PADDING); if (!BCRYPT_SUCCESS(status)) goto Cleanup; // 更新为实际明文长度,避免多余字节 *pcbPlaintext = cbResult; Cleanup: if (hKey) BCryptDestroyKey(hKey); if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0); return status; } int main() { // 示例数据(对应Python AES-GCM加密后的解码结果) BYTE key[AES_256_KEY_SIZE] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F}; BYTE iv[GCM_IV_SIZE] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B}; // 密文(已分离末尾16字节的标签) BYTE ciphertext[] = {0x69,0xC4,0xE0,0xD8,0x6A,0x7B,0x04,0x30,0xD8,0xCD,0xB7,0x80,0x70,0xB4,0xC5,0x5A}; DWORD cbCiphertext = sizeof(ciphertext); BYTE tag[GCM_TAG_SIZE] = {0x1A,0x80,0x7D,0x27,0x2B,0x45,0xBC,0xBF,0xE0,0x79,0xAD,0x27,0xDA,0xAF,0x5F,0x77}; // 附加数据(加密时如果用了,解密必须传完全相同的内容;无则设为NULL,长度0) BYTE additionalData[] = {0x41,0x54,0x54,0x41}; DWORD cbAdditionalData = sizeof(additionalData); BYTE plaintext[1024] = {0}; DWORD cbPlaintext = sizeof(plaintext); NTSTATUS status = AES256GCM_Decrypt(key, iv, ciphertext, cbCiphertext, tag, additionalData, cbAdditionalData, plaintext, &cbPlaintext); if (BCRYPT_SUCCESS(status)) { printf("解密成功,明文:%.*s\n", cbPlaintext, plaintext); } else { printf("解密失败,错误码:0x%X\n", status); } return 0; }
关键注意事项
- 标签分离:Python的AES-GCM加密输出一般是
密文+标签的拼接形式,解密前必须把最后16字节的标签单独提取出来 - 结构体初始化:
BCRYPT_INIT_AUTH_MODE_INFO宏会自动设置结构体的版本和大小字段,这是避免参数错误的核心 - 输出长度控制:先调用
BCryptDecrypt获取准确的明文长度,再分配对应缓冲区,就能解决多余字节问题 - 附加数据一致性:加密时使用的附加数据(AD),解密时必须完全一致,否则认证会失败
内容的提问来源于stack exchange,提问作者exomm
相关产品推荐
相关产品推荐

