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

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字节,无需通过加密空数据获取。

解决步骤

  1. 固定块大小:直接使用AES标准块大小16字节,或通过CryptGetKeyParam动态获取,避免依赖错误的CryptEncrypt结果。
  2. 分离长度变量:用独立变量存储每次读取的字节数和解密后的字节数,防止块大小被篡改。
  3. 正确设置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 11:13:09