将证书从智能卡添加到本地用户存储时,我们如何解决CryptAcquireCertificatePrivateKey
失败?0x8009200B
在用户向 CA 在线生成智能卡证书请求的注册系统中,证书在智能卡中“离线”加载,例如在请求发出几天后,因此certenrolllib
用于创建请求的对象不能用于安装卡上的证书和卡生成的私钥永远不会也不能导出到卡外。
当我们在卡中加载证书时,我们使用 Minidriver API,我们有用于生成密钥的密钥容器的名称(通常是 GUID,类似lr-e46f1586-7133-4284-895d-557e2261c24d
)我们开始读取 cmapfile 并获得密钥容器与该 GUID 对应的索引 XX,我们在卡微型驱动程序文件系统的 mscp 文件夹中创建一个 kscXX 证书,我们使用 zlib 压缩从 CA 获得的 DER 二进制证书,我们添加一个标头并将其加载到 kscXX 文件中。
我们可以看到卡上证书的格式很好,我们可以使用各种工具查看它。问题是此证书未出现在 Microsoft 用户存储中。事实上,它“可能”有时会出现,特别是如果容器是默认容器,但无论如何不会立即出现,也不会出现在所有操作系统中。
我们使用 MSCAPI 创建了一个库,它获取证书上下文并通过在以下代码中使用CryptAcquireCertificatePrivateKey来证明私钥所有权(例如:addCardCertToStore.exe 工具)
使用 PIN 登录、获取用户密钥等:
fStatus = CryptGetKeyParam(
hKey, // HCRYPTKEY hKey,
KP_CERTIFICATE, // DWORD dwParam,
pCertBlob, // BYTE* pbData,
&dwCertLen, // DWORD* pdwDataLen,
0 // DWORD dwFlags
);
if (!fStatus)
{
return 1;
}
pCertContext = CertCreateCertificateContext(
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
pCertBlob,
dwCertLen);
//trying to prove privatekey ownership by calling CryptAcquireCertificatePrivateKey
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCrypt;
DWORD hInfo;
fStatus=CryptAcquireCertificatePrivateKey(pCertContext,0,NULL,&hCrypt,&hInfo,NULL);
if (!fStatus)
{
return 1;
}
hStoreHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, L"My");
// 保存证书上下文以存储
如果CryptAcquireCertificatePrivateKey
未调用,则证书始终导出到存储,但没有私钥所有权,然后不能用于签名等操作。
但是,当我们CryptAcquireCertificatePrivateKey
确实使用“50% 的时间”时,我们可以将证书保存到与私钥所有权一起存储,但并非总是如此,在某些情况下,我们会看到0x8009200B
错误(CRYPT_E_NO_KEY_PROPERTY,例如“找不到用于解密的证书和私钥。 ') 调用时CryptAcquireCertificatePrivateKey
。例如第一次启动 addCardCertToStore.exe 时可能会发生此错误,然后如果我们第二次重新启动 addCardCertToStore.exe 它可能会工作,CryptAcquireCertificatePrivateKey
返回 OK,并且证书已安装在商店中,有时我们必须调用它 3 次,有时似乎它永远不会完成,有时在我们重新插入卡后它可以工作,有时它会失败,因为证书在商店中一段时间后自动安装并删除了此证书CryptAcquireCertificatePrivateKey
工作并再次在商店中添加证书。
我知道 CSP 正在维护一个缓存,并且它会定期扫描卡以更新该缓存,并且它可能会或可能不会检测到卡上的新证书并将其单独添加到存储中,并且某些 MSCAPI 操作可能会产生以下效果让 CSP 独立于操作本身从卡证书更新存储。
最后的问题是:为什么会出现这种奇怪的行为,CryptAcquireCertificatePrivateKey
以及在这种情况下我们如何解释错误代码0x8009200B
(访问被拒绝)?
另一点是我们是否应该在卡上安装 DER 证书而不是使用 minidriver API 而是使用 MSCAPI 函数(如果可能的话)?
我们怎样才能有一个稳定的方式将卡证书添加到商店?