我有一个用于导入ECC证书私钥的代码(如下)
该代码在以下 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 lib(代码如下)生成和安装的证书时,代码失败并显示 NTE_NOT_SUPPORTED。
这是用于使用 openssl 和 Windows API 安装证书的代码(此处省略了错误处理以使其更短):
// 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 查看证书,我可以看到这一点:
私钥不可以纯文本形式导出
PFXImportCertStore 将 CRYPT_EXPORTABLE 视为 CNG 标志 NCRYPT_ALLOW_EXPORT_FLAG;但按照您请求的方式导出需要更强的 NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG。
如果不允许明文导出,则只能导出为加密形式,例如加密的 PKCS8。 https://stackoverflow.com/a/55249105/6535399 展示了如何使用 C# 中的 P/Invokes 来做到这一点,但它应该很容易转换回 C。
您可能还会发现改变关键出口政策是有效的。 如果导入后它仍然是可替换的(我不记得它是不是,或者它是否取决于您使用的 Windows 版本),您可以抓住密钥句柄并更改 NCRYPT_EXPORT_POLICY_PROPERTY 来断言NCRYPT_ALLOW_EXPORT_FLAG 和 NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG。 如果您完全可以做到这一点,则只能在访问密钥数据的第一次调用之前(签名、验证、加密、解密或导出)。