2
votes

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?

1
I would say that depends on the used CSP. If the certificate is managed by a CSP for USB tokens it expects the private key to be present on an USB token.Robert
What mechanism CSP found and access the private key for cryptographic operations? Can you give me more detail?Thinh Nguyen Van
I guess it depends on the location and type of the keystore used by CSP, not on the CSP itself. For example a USB token usually requires a PKCS#11 driver. The CSP "uses" the key through the driver but does not "get" it because a private key can not be extracted from token. However a software keystore can provide the key or allow to use it without exposing the content( like windows keystore) Is this what you are asking for?pedrofb
@pedrofb Yeah! When a certificate is selected for signing, how does the CSP know what private key to use?Thinh Nguyen Van
The USB stick does provide both certificate and private key, both are related. When you know what certificate should be used you automatically know what key to use.Robert

1 Answers

4
votes

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):

test_cert

When Windows wants to get private key, it calls CryptAcquireCertificatePrivateKey which in turn calls CertGetCertificateContextProperty(..., CERT_KEY_PROV_INFO_PROP_ID, ...).