My question is: when an application invoke CSP for performing cryptographic operation, such as signing, how CSP find private key of certifcate respectively?
If a certificate imported to cert store that the private key not in local computer (on USB token, external storage, e.g. mobile device), it can found?
When you import a certificate to the system store, Windows creates a BLOB structure that contains an encoded certificate itself and it properties. BLOB has following structure:
property1_id (4 bytes)
reserved = 0x00000001
property1_length (4 bytes)
property1_data[property1_length]
...
cert_property_id = 0x00000020
reserved = 0x00000001
cert_data_length (4 bytes)
cert_data[cert_data_length]
Therefore, if you want that your imported certificate to have link to a private key, you need to set CERT_KEY_PROV_INFO_PROP_ID. You can achieve that with CRYPT_KEY_PROV_INFO structure and CertSetCertificateContextProperty function.
For example:
#include <Windows.h>
#include <wincrypt.h>
void SetKeyLink()
{
HCERTSTORE hStore = NULL;
CRYPT_KEY_PROV_INFO key_prov_info = { 0 };
PCCERT_CONTEXT pCertContext = nullptr;
std::vector<BYTE> der_encoded_cert;
hStore = CertOpenSystemStore(NULL, L"MY");
if (!hStore)
{
goto Exit;
}
der_encoded_cert = LoadFromFile();
pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, der_encoded_cert.data(), der_encoded_cert.size());
if (!pCertContext)
{
goto Exit;
}
/* For legacy CSP */
key_prov_info.dwProvType = PROV_RSA_AES; // Or YOUR_PROVIDER_TYPE
key_prov_info.dwKeySpec = AT_SIGNATURE; // Or AT_KEYEXCHANGE
key_prov_info.pwszContainerName = L"Your_key_name";
key_prov_info.dwFlags = CERT_SET_KEY_PROV_HANDLE_PROP_ID;
key_prov_info.cProvParam = 0;
key_prov_info.pwszProvName = nullptr;
key_prov_info.rgProvParam = 0;
/*
Or if you use CNG Key storage provider:
// Or L"Your_CNG_key_storage_provider_name"
key_prov_info.pwszProvName = L"Microsoft Software Key Storage Provider";
key_prov_info.pwszContainerName = L"Your_key_name";
key_prov_info.dwFlags = CERT_SET_KEY_PROV_HANDLE_PROP_ID;
key_prov_info.dwProvType = 0;
key_prov_info.dwKeySpec = 0;
key_prov_info.cProvParam = 0;
key_prov_info.rgProvParam = 0;
*/
if (!CertSetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &key_prov_info))
{
goto Exit;
}
if (!CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_ALWAYS, NULL))
{
goto Exit;
}
std::cout << "success";
Exit:
if (pCertContext)
{
CertFreeCertificateContext(pCertContext);
}
if (hStore)
{
CertCloseStore(hStore, 0);
}
return;
}
For now your certificate will look something like this (sorry for not English):
When Windows wants to get private key, it calls CryptAcquireCertificatePrivateKey
which in turn calls CertGetCertificateContextProperty(..., CERT_KEY_PROV_INFO_PROP_ID, ...)
.