NCryptExportKey返回NTE_NOT_SUPPORTED:OpenSSL生成ECC证书的密钥导出问题
问题描述
我编写了一段导入ECC证书私钥的代码,使用PowerShell脚本生成的ECDSA_P256证书时运行正常,但换成OpenSSL生成并安装的证书后,代码执行失败,返回NTE_NOT_SUPPORTED错误。
PowerShell生成证书的脚本
# Define certificate properties $certProps = @{ Subject = "CN=GSA_ECC.com" KeyAlgorithm = "ECDSA_P256" KeyLength = 256 CertStoreLocation = "Cert:\LocalMachine\My" FriendlyName = "MyCertificate" KeyExportPolicy="Exportable" ` } # Create a self-signed certificate New-SelfSignedCertificate @certProps
OpenSSL生成并安装证书的代码(省略错误处理)
// Create a PKCS#12 container PKCS12* p12 = PKCS12_create("Password", "Friendly Name", pkey, cert, NULL, 0, 0, 0, 0, 0); // Convert PKCS#12 container to DER format unsigned char* der_data = NULL; int der_len = i2d_PKCS12(p12, &der_data); // Prepare CRYPT_DATA_BLOB CRYPT_DATA_BLOB pfx_blob; pfx_blob.cbData = der_len; pfx_blob.pbData = der_data; // Import the PKCS#12 container into a temporary store HCERTSTORE hTempStore = PFXImportCertStore(&pfx_blob, L"Password", CRYPT_EXPORTABLE | PKCS12_ALLOW_OVERWRITE_KEY); // Open the target certificate store // Open the "MY" certificate store in the Local Machine context HCERTSTORE hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"MY" ); PCCERT_CONTEXT ctx = CertEnumCertificatesInStore(hTempStore, NULL); auto res = CertAddCertificateContextToStore(hStore, ctx, CERT_STORE_ADD_REPLACE_EXISTING, NULL);
加载私钥的代码(报错位置已标注)
if (strcmp(_pCertContext->pCertInfo->SignatureAlgorithm.pszObjId, szOID_ECDSA_SHA256)) { throw std::runtime_error("Unsupported signature algorithm for private key extraction"); } BOOL result = CryptAcquireCertificatePrivateKey( _pCertContext, CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &key, &dwKeySpec, &freeKey ); // error if result = 0 if (dwKeySpec == CERT_NCRYPT_KEY_SPEC) { DWORD keySize = 0; // Determine the size of the output buffer - This fails! auto result = NCryptExportKey(key, NULL, BCRYPT_ECCPRIVATE_BLOB, NULL, NULL, 0, &keySize, 0); }
额外信息
使用certutil查看该证书时显示:
Private key is NOT plain text exportable
解决方案
问题核心在于OpenSSL生成的私钥导入Windows时未正确设置可导出属性,且CNG对OpenSSL生成的EC密钥格式兼容性需调整,以下是具体修复步骤:
1. 调整OpenSSL生成PKCS#12的参数
调用PKCS12_create时添加PKCS12_KEY_EXP标志,确保私钥标记为可导出:
PKCS12* p12 = PKCS12_create("Password", "Friendly Name", pkey, cert, NULL, PKCS12_KEY_EXP, 0, 0, 0, 0);
该标志会在PKCS#12容器中标记私钥允许导出,让Windows导入时识别到可导出属性。
2. 优化导入时的密钥策略
在PFXImportCertStore调用中添加CRYPT_MACHINE_KEYSET,确保密钥存储在本地机器上下文,避免用户权限问题:
HCERTSTORE hTempStore = PFXImportCertStore(&pfx_blob, L"Password", CRYPT_EXPORTABLE | PKCS12_ALLOW_OVERWRITE_KEY | CRYPT_MACHINE_KEYSET);
3. 规范OpenSSL生成EC密钥的流程
使用以下命令生成兼容Windows的EC密钥和PFX文件,确保曲线为prime256v1且密钥格式符合CNG要求:
# 生成EC密钥 openssl ecparam -name prime256v1 -genkey -noout -out ec.key # 生成证书请求 openssl req -new -key ec.key -out ec.csr # 生成自签名证书 openssl x509 -req -days 365 -in ec.csr -signkey ec.key -out ec.crt # 打包为PFX openssl pkcs12 -export -out ec.pfx -inkey ec.key -in ec.crt -password pass:Password
4. 显式设置密钥导出权限
如果上述步骤无效,在导出私钥前显式设置密钥的导出策略:
DWORD exportPolicy = NCRYPT_ALLOW_EXPORT_FLAG; NCryptSetProperty(key, NCRYPT_EXPORT_POLICY_PROPERTY, (PBYTE)&exportPolicy, sizeof(exportPolicy), 0); // 执行导出 auto result = NCryptExportKey(key, NULL, BCRYPT_ECCPRIVATE_BLOB, NULL, NULL, 0, &keySize, 0);
内容的提问来源于stack exchange,提问作者sara
相关产品推荐
相关产品推荐

