How can we solve CryptAcquireCertificatePrivateKey
fails with 0x8009200B
when adding certificate from smartcard to local user store?
In an enrollment system where users generate smartcard certificate request online to a CA, the certificate is loaded 'offline' in the smartcard, for example several days after the request was issued so the certenrolllib
objects used for the creation of the request cannot be used for installing the certificate on the card and the card generated the private key which will never and cannot anyway be exported outside of the card.
When we load the certificate in the card, we use the Minidriver API, we have the name of the key container that was used for generation of the key (usually a GUID, something like lr-e46f1586-7133-4284-895d-557e2261c24d
) we start to read the cmapfile and we get the keycontainer index XX that corresponds to that GUID and we create a kscXX certificate in the mscp folder in the card minidriver filesystem, we compress with zlib the DER binary certificate obtained from the CA, we add a header and we load that on the kscXX file.
We can see that the on card certificate is well formed and that we can see it using various tools. The problem is that this certificate does not appear on the Microsoft user store. In fact it 'may' appear sometimes especially if the container is the default container, but anyway not immediately and not in all OS.
We created a lib using MSCAPI that get the certificate context and that proves private key ownership by using CryptAcquireCertificatePrivateKey in the following code (used in say: addCardCertToStore.exe tool for example)
Logging with PIN, getting the userkey etc.:
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");
// saving certificate context to store
If CryptAcquireCertificatePrivateKey
is not called, then the certificate is always exported to store, but without private key ownership and then cannot be used for operations like signature etc.
However, when we use CryptAcquireCertificatePrivateKey
indeed '50% of the times' we can save the certificate to store with private key ownership, but not always, in several case we see 0x8009200B
error (CRYPT_E_NO_KEY_PROPERTY , e.g. 'Cannot find the certificate and private key for decryption.') when call CryptAcquireCertificatePrivateKey
. This error may happen for example the first time we launch the addCardCertToStore.exe then if we restart addCardCertToStore.exe a second time it may work, CryptAcquireCertificatePrivateKey
returns OK, and certificate is installed in the store, sometimes we have to call it three times, sometimes it seems it will never complete and sometimes it works after we reinsert the card, sometimes it fails because the certificate was installed automatically after a while in the store and by deleting this certificate CryptAcquireCertificatePrivateKey
works and add again the certificate on the store.
I know that the CSP is maintaining a cache and that it scans periodically the card to update that cache and that it may or may not detect a new certificate on card and add it to the store alone and that some MSCAPI operation can have the effect of having the CSP updating the store from the card certificates independently of the operation itself.
At the end the question is: why such a strange behaviour of CryptAcquireCertificatePrivateKey
and how can we interpret the error code 0x8009200B
(access denied) in that context?
And other point is that should we install the DER certificate on card not by using minidriver API but by using MSCAPI functions (if this is possible)?
How can we have a steady way of adding the card certificate to the store?