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

C#生成ECDSA签名后,Windows Crypto API在C中导入公钥失败求助

解决Windows CAPI导入CNG ECC公钥的NTE_BAD_VER错误

你遇到的0x80090007 (NTE_BAD_VER)错误,核心原因是CNG和传统CryptoAPI的密钥格式不兼容,加上代码里的几个关键错误,具体分析和修复方案如下:

错误原因拆解

  • API体系不匹配:
    C#代码使用的是CNG(Cryptography API: Next Generation)生成和导出ECC密钥(CngKey、ECDsaCng),导出的EccPublicBlob是CNG专属的密钥格式。但你的C代码用的是传统CryptoAPI(CryptAcquireContext、CryptImportKey),这套老API无法识别CNG的ECC密钥Blob格式,直接导入就会触发版本不匹配的错误。
  • 错误的加密服务提供者(CSP):
    你指定的PROV_RSA_FULL是针对RSA算法的CSP,完全不支持ECC密钥。即使是传统CryptoAPI里支持ECC的CSP(比如PROV_ECC_AES),也无法直接处理CNG导出的Blob。
  • 笔误导致的Blob长度错误:
    代码里写了sizeof(DesKeyBlob),但你的变量名是KeyBlob,这会传入错误的密钥Blob长度,进一步加剧导入失败。

修复方案:使用CNG API在C中导入公钥并验证签名

既然C#用的是CNG,C代码也应该用CNG的BCrypt系列函数来处理,这样格式完全兼容。以下是修正后的完整C代码:

#include <windows.h>
#include <bcrypt.h>
#include <stdio.h>

#pragma comment(lib, "bcrypt.lib")

int main() {
    // 替换为你从C#导出的公钥字节数组
    BYTE publicKeyBlob[] = {69,67,83,49,32,0,0,0,227,146,138,255,218,235,122,141,44,110,211,95,59,227,226,163,81,188,242,115,60,171,46,141,221,117,169,139,42,143,67,85,144,242,232,188,22,158,230,15,110,6,214,252,252,242,224,241,110,186,1,244,176,65,88,184,94,19,98,174,158,7,154,152};
    DWORD publicKeyBlobLen = sizeof(publicKeyBlob);

    // 替换为C#生成的签名字节数组
    BYTE signature[] = { /* 你的签名数据 */ };
    DWORD signatureLen = sizeof(signature);

    // 待验证的原始数据(和C#中SignData的输入一致:{1,2,3,4,5})
    BYTE data[] = {1,2,3,4,5};
    DWORD dataLen = sizeof(data);

    BCRYPT_ALG_HANDLE hAlg = NULL;
    BCRYPT_KEY_HANDLE hKey = NULL;
    NTSTATUS status = STATUS_UNSUCCESSFUL;

    // 1. 打开ECDSA P256算法提供者
    status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_ECDSA_ALGORITHM, NULL, 0);
    if (!BCRYPT_SUCCESS(status)) {
        printf("BCryptOpenAlgorithmProvider failed: 0x%08x\n", status);
        goto Cleanup;
    }

    // 2. 设置算法为P256曲线
    status = BCryptSetProperty(hAlg, BCRYPT_ECC_CURVE_NAME, (PUCHAR)BCRYPT_SECP256R1_ALGORITHM, sizeof(BCRYPT_SECP256R1_ALGORITHM)-1, 0);
    if (!BCRYPT_SUCCESS(status)) {
        printf("BCryptSetProperty failed: 0x%08x\n", status);
        goto Cleanup;
    }

    // 3. 导入CNG格式的ECC公钥
    status = BCryptImportKeyPair(hAlg, NULL, BCRYPT_ECCPUBLIC_BLOB, &hKey, publicKeyBlob, publicKeyBlobLen, 0);
    if (!BCRYPT_SUCCESS(status)) {
        printf("BCryptImportKeyPair failed: 0x%08x\n", status);
        goto Cleanup;
    }

    // 4. 验证签名
    status = BCryptVerifySignature(hKey, NULL, data, dataLen, signature, signatureLen, 0);
    if (BCRYPT_SUCCESS(status)) {
        printf("Signature verified successfully!\n");
    } else {
        printf("Signature verification failed: 0x%08x\n", status);
    }

Cleanup:
    // 释放资源
    if (hKey) BCryptDestroyKey(hKey);
    if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
    return status == STATUS_SUCCESS ? 0 : 1;
}

关键注意事项

  • 密钥Blob格式匹配:C#导出的EccPublicBlob对应CNG API中的BCRYPT_ECCPUBLIC_BLOB标识,所以BCryptImportKeyPair中要指定这个值。
  • 曲线一致性:C#用的是ECDsaP256(对应SECP256R1曲线),所以C代码中要设置BCRYPT_SECP256R1_ALGORITHM,确保曲线参数一致。
  • 签名格式:C#的SignData默认生成的是IEEE P1363格式的签名,CNG的BCryptVerifySignature默认支持这种格式,不需要额外设置。

如果一定要用传统CryptoAPI,你需要在C#中把公钥导出为PKCS#1或X.509格式(而不是CNG的EccPublicBlob),然后在C代码中用CryptImportPublicKeyInfo导入,但这种方式步骤更繁琐,且传统CryptoAPI对ECC的支持不如CNG完善,更推荐使用上述CNG方案。

内容的提问来源于stack exchange,提问作者Regis Portalez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:25:22