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
相关产品推荐
相关产品推荐

