AES解密代码可正常运行但CryptDecrypt返回错误0x80090005
问题原因及解决方法
核心问题分析
你遇到的0x80090005错误对应NTE_BAD_DATA,根源是代码的三个关键逻辑错误:
- 错误设置
FINAL参数:每次调用CryptDecrypt都将倒数第二个参数设为TRUE,但该参数仅应在处理最后一个数据块时设为TRUE。中间块设TRUE会让加密服务提供者(CSP)错误判定为最终块,校验填充时触发NTE_BAD_DATA。 - 块大小变量被篡改:复用
numBytes变量同时作为fread读取长度和CryptDecrypt的输入输出参数。CryptDecrypt会将其修改为解密后的实际字节数(比如去除PKCS7填充后的长度),导致后续fread读取长度混乱。 - 不合理获取块大小:用
CryptEncrypt获取块大小的方式无意义,AES块大小固定为16字节,无需通过加密空数据获取。
解决步骤
- 固定块大小:直接使用AES标准块大小16字节,或通过
CryptGetKeyParam动态获取,避免依赖错误的CryptEncrypt结果。 - 分离长度变量:用独立变量存储每次读取的字节数和解密后的字节数,防止块大小被篡改。
- 正确设置
FINAL参数:仅当读取字节数小于块大小(即最后一块)或文件读取结束时,将CryptDecrypt的FINAL参数设为TRUE,其余情况设为FALSE。
修正后的代码
#include <stdio.h> #include <stdlib.h> #include <windows.h> #include <wincrypt.h> void error(const char *what) { fprintf(stderr, "%s failed with last error 0x%x\n", what, GetLastError()); exit(1); } #define AES_KEY_SIZE 32 #define AES_BLOCK_SIZE 16 // AES固定块大小为16字节 typedef struct { BLOBHEADER hdr; DWORD dwKeySize; BYTE rgbKeyData[AES_KEY_SIZE]; } AES256KEYBLOB; BYTE *hex2byte(const char *hex) { int len = strlen(hex) / 2; BYTE *bytes = malloc(len); if (bytes == NULL) { error("malloc"); return NULL; } unsigned char val; for (int i = 0; i < len; i++) { sscanf(&hex[i * 2], "%2hhx", &val); bytes[i] = val; } return bytes; } int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "Usage: %s <input_file> <output_file>\n", argv[0]); return 1; } // 原十六进制密钥仅31字节,AES-256需32字节,此处补全一个0(请根据实际密钥调整) BYTE *key = hex2byte("c2ecf7a4f3d1620f18cd38379269948fcd3aaf4fdce6d50d310464ea823a00"); AES256KEYBLOB aes256KeyBlob; aes256KeyBlob.hdr.bType = PLAINTEXTKEYBLOB; aes256KeyBlob.hdr.bVersion = CUR_BLOB_VERSION; aes256KeyBlob.hdr.reserved = 0; aes256KeyBlob.hdr.aiKeyAlg = CALG_AES_256; aes256KeyBlob.dwKeySize = AES_KEY_SIZE; memcpy(aes256KeyBlob.rgbKeyData, key, AES_KEY_SIZE); HCRYPTPROV hProv; if (!CryptAcquireContextA(&hProv, NULL, MS_ENH_RSA_AES_PROV_A, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { error("CryptAcquireContext"); } HCRYPTKEY hKey; if (!CryptImportKey(hProv, (BYTE *) &aes256KeyBlob, sizeof(AES256KEYBLOB), 0, CRYPT_EXPORTABLE, &hKey)) { CryptReleaseContext(hProv, 0); error("CryptImportKey"); } FILE *input_file = fopen(argv[1], "rb"); if (!input_file) { perror("Error opening input file"); return 1; } FILE *output_file = fopen(argv[2], "wb"); if (!output_file) { perror("Error opening output file"); fclose(input_file); return 1; } BYTE blk[AES_BLOCK_SIZE]; DWORD bytes_read; while ((bytes_read = fread(blk, 1, AES_BLOCK_SIZE, input_file)) > 0) { BOOL is_final = (bytes_read < AES_BLOCK_SIZE) || (feof(input_file)); DWORD decrypted_len = bytes_read; if (!CryptDecrypt(hKey, 0, is_final, 0, blk, &decrypted_len)) { error("CryptDecrypt"); } fwrite(blk, 1, decrypted_len, output_file); } free(key); CryptDestroyKey(hKey); CryptReleaseContext(hProv, 0); fclose(input_file); fclose(output_file); return 0; }
额外说明
- 原代码中的十六进制密钥仅31字节,不符合AES-256对32字节密钥的要求,修正时补了一个
0,请根据实际密钥调整长度。 - 若加密时使用了自定义填充模式,需确保解密时填充模式一致,否则仍可能触发
NTE_BAD_DATA错误。
内容的提问来源于stack exchange,提问作者Zibri
相关产品推荐
相关产品推荐

