Self-signed cert generated through CryptAPI and added to a remote certificate store does not have private key

1.3k Views Asked by At

I have a scenario wherein an application needs to create a self-signed certificate and add it to the certificate store on a remote machine. I've tried both CryptAPI and CNG (though CNG still appears to use CryptAPI for creating the self-signed certificate and adding it to the remote certificate store), but the behavior I see occurs in both.

Environment:

Two machines on the same domain. One is Windows Server 2016 Standard and the other is Windows Server 2019 Datacenter. Same domain user with admin privileges used to log into both machines. Run the application on the 2016 machine indicating the hostname of the other.

The code uses MFC/ATL for CString, includes wincrypt.h, and links against crypt32.lib. Tested against both VS2019's toolset and VS2005's toolset.

What I see:

The self-signed certificate is created and added to the remote store. If I view the certificate through the MMC it indicates that it has a private key attached. However, when I try to click "Manage Private Keys..." an error dialog says:

"No keys found for certificate!"

Similarly the Certificate Export Wizard says:

"Note: The associated private key cannot be found. Only the certificate can be exported."

And the "Yes, export the private key" option is greyed out.

My theory is that the private key isn't being properly attached to the certificate context (or otherwise not being transmitted to the remote machine when I add the key to the certificate store).

Here's what my code looks like:

#include <afx.h>
#include <wincrypt.h>

#pragma comment(lib, "crypt32.lib")

CString GetCertificateThumbprintString(PCCERT_CONTEXT pCertContext)
{
    DWORD cbSize;
    if (!CryptHashCertificate(0, 0, 0, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded, NULL, &cbSize)) {
        return "";
    }

    LPSTR pszString;
    if (!(pszString = (LPSTR)malloc(cbSize * sizeof(TCHAR)))) {
        return "";
    }

    if (!CryptHashCertificate(0, 0, 0, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded, (BYTE*)pszString, &cbSize)) {
        free(pszString);
        return "";
    }
    else {
        pszString[cbSize] = NULL;
        LPTSTR lpszTP = NULL;
        if (!(lpszTP = (LPTSTR)malloc((cbSize + 1) * sizeof(TCHAR) * 2))) {
            free(pszString);
            return "";
        }

        for (int i = 0; i < (int)cbSize; ++i) {
            _stprintf_s(&lpszTP[i * 2], sizeof(TCHAR) + 1, _T("%.2X"), pszString[i] & 0xff);
        }

        CString result = lpszTP;

        free(lpszTP);
        free(pszString);

        return result;
    }
}

CString CreateCertificate(CString hostName)
{
    wchar_t subjectName [MAX_PATH] = L"";
    wchar_t store       [MAX_PATH] = L"";

    wsprintfW(store, L"\\\\%s\\MY", hostName);
    wsprintfW(subjectName, L"CN=%s", hostName);

    HCERTSTORE certStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, store);
    if (NULL == certStore) {
        _tprintf(_T("Failed to open Personal certificate store of %s."), hostName);
        return "";
    }

    DWORD encodedSubjectSize = 0;
    if (!CertStrToName(X509_ASN_ENCODING, subjectName, CERT_X500_NAME_STR, NULL, NULL, &encodedSubjectSize, NULL)) {
        _tprintf(_T("Invalid certificate subject name. Error %d\n"), GetLastError());
        return "";
    }

    BYTE* encodedSubject = (BYTE*)malloc(encodedSubjectSize);
    if (NULL == encodedSubject) {
        _tprintf(_T("malloc() failed: %d "), GetLastError());
        return "";
    }

    if (!CertStrToName(X509_ASN_ENCODING, subjectName, CERT_X500_NAME_STR, NULL, encodedSubject, &encodedSubjectSize, NULL)) {
        _tprintf(_T("Invalid certificate subject name. Error %d\n"), GetLastError());
        free(encodedSubject);
        return "";
    }

    // Acquire key container
    HCRYPTPROV cryptProvider;
    const wchar_t* pszKeyContainerName = L"TESTKEYCONTAINERTEST";
    if (!CryptAcquireContext(&cryptProvider, pszKeyContainerName, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET)) {
        if (GetLastError() == NTE_EXISTS)
        {
            if (!CryptAcquireContext(&cryptProvider, pszKeyContainerName, NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
            {
                _tprintf(_T("Can't get a crypto provider. Error %d\n"), GetLastError());
                free(encodedSubject);
                return "";
            }
        }
    }

    // Generate new key pair
    HCRYPTKEY key;
    if (!CryptGenKey(cryptProvider, AT_SIGNATURE, 0x08000000 /* RSA-2048-BIT_KEY */ | CRYPT_EXPORTABLE, &key)) {
        _tprintf(_T("Can't generate a key pair. Error %d\n"), GetLastError());

        CryptReleaseContext(cryptProvider, 0);
        CertCloseStore(certStore, 0);
        free(encodedSubject);

        return "";
    }

    // Prepare key provider structure for self-signed certificate
    CRYPT_KEY_PROV_INFO keyProviderInfo;
    ZeroMemory(&keyProviderInfo, sizeof(keyProviderInfo));
    keyProviderInfo.pwszContainerName = (LPWSTR)pszKeyContainerName;
    keyProviderInfo.dwProvType = PROV_RSA_FULL;
    keyProviderInfo.dwFlags = CRYPT_MACHINE_KEYSET;
    keyProviderInfo.dwKeySpec = AT_SIGNATURE;

    // Prepare algorithm structure for self-signed certificate
    CRYPT_ALGORITHM_IDENTIFIER algorithm;
    memset(&algorithm, 0, sizeof(algorithm));
    algorithm.pszObjId = (LPSTR)szOID_RSA_SHA256RSA;

    // Prepare certificate Subject for self-signed certificate
    CERT_NAME_BLOB subjectBlob;
    ZeroMemory(&subjectBlob, sizeof(subjectBlob));
    subjectBlob.cbData = encodedSubjectSize;
    subjectBlob.pbData = encodedSubject;

    PCCERT_CONTEXT certContext = CertCreateSelfSignCertificate(NULL, &subjectBlob, 0, &keyProviderInfo, &algorithm, NULL, NULL, NULL);
    if (!certContext) {
        _tprintf(_T("Can't create a self-signed certificate. Error %d\n"), GetLastError());

        CryptDestroyKey(key);
        CryptReleaseContext(cryptProvider, 0);
        CertCloseStore(certStore, 0);
        free(encodedSubject);

        return "";
    }

    if (!CertSetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &keyProviderInfo)) {
        _tprintf(_T("Unable to set key provider info property on certificate context. Error %d\n"), GetLastError());

        CertFreeCertificateContext(certContext);
        CryptDestroyKey(key);
        CryptReleaseContext(cryptProvider, 0);
        CertCloseStore(certStore, 0);
        free(encodedSubject);

        return "";
    }

    // add certificate to store
    if (!CertAddCertificateContextToStore(certStore, certContext, CERT_STORE_ADD_ALWAYS, nullptr))
    {
        CertFreeCertificateContext(certContext);
        CryptDestroyKey(key);
        CryptReleaseContext(cryptProvider, 0);
        CertCloseStore(certStore, 0);
        free(encodedSubject);

        return "";
    }

    CString result = GetCertificateThumbprintString(certContext);

    CertFreeCertificateContext(certContext);
    CryptDestroyKey(key);
    CryptReleaseContext(cryptProvider, 0);
    CertCloseStore(certStore, 0);
    free(encodedSubject);

    return result;
}

int main(int argc, char* argv[])
{
    if (argc < 2) {
        printf("Need arg.");
        return -1;
    }

    auto index = 1;
    if (strcmp(argv[1], "dbg") == 0) {
        while (!IsDebuggerPresent()) Sleep(100);

        index = 2;
    }

    CString strThumbprint = CreateCertificate(argv[index]);
    _tprintf(strThumbprint);

    return 0;
}

I found a similar issue here which is what gave me the idea to call CertSetCertificateContextProperty in the first place. But it doesn't resolve the issue.

What am I missing here?

Edit: This same code works when the certificate store opened is on the local machine. This issue only arises when the certificate store is on a remote machine.

Edit 2: At RbMm's suggestion, I investigated exporting the certificate into a PFX. It was unclear to me how exporting the certificates into PFX's would change anything except that perhaps the generated PFX would do some magic to allow the key pair to be passed along when I insert it into the remote store.

In that investigation I found this which helped me change my code to utilize PFXExportCertStoreEx/PFXImportCertStore. What you see below is what I added (replacing the CertSetCertificateContextProperty call). However, it should be noted that this did not work either. So I'm again at a loss.

// Create temporary store to shove the self-signed certificate into.
HCERTSTORE initialTempStore = CertOpenStore(CERT_STORE_PROV_MEMORY, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"MY");
if (NULL == initialTempStore) {
    _tprintf(_T("Failed to open local Personal certificate store."));

    CertFreeCertificateContext(certContext);
    CryptDestroyKey(key);
    CryptReleaseContext(cryptProvider, 0);
    free(encodedSubject);

    return "";
}

// Add the certificate to the self-signed store.
if (!CertAddCertificateContextToStore(initialTempStore, certContext, CERT_STORE_ADD_REPLACE_EXISTING, nullptr)) {
    _tprintf(_T("Failed to add cert to local store."));

    CertCloseStore(initialTempStore, 0);
    CertFreeCertificateContext(certContext);
    CryptDestroyKey(key);
    CryptReleaseContext(cryptProvider, 0);
    free(encodedSubject);

    return "";
}

// Export the certificate store into a PFX that packages the certificates with the private keys.
CRYPT_DATA_BLOB pfx;
ZeroMemory(&pfx, sizeof(CRYPT_DATA_BLOB));
LPCTSTR password = L"hello5";
if (!PFXExportCertStoreEx(initialTempStore, &pfx, password, NULL, EXPORT_PRIVATE_KEYS)) {
    _tprintf(_T("Unable to export PFX.\n"));

    CertCloseStore(initialTempStore, 0);
    CertDeleteCertificateFromStore(certContext);
    CertFreeCertificateContext(certContext);
    CryptDestroyKey(key);
    CryptReleaseContext(cryptProvider, 0);
    free(encodedSubject);

    return "";
}

pfx.pbData = (BYTE*)malloc(pfx.cbData);
if (!PFXExportCertStoreEx(initialTempStore, &pfx, password, NULL, EXPORT_PRIVATE_KEYS)) {
    _tprintf(_T("Unable to export PFX.\n"));

    CertDeleteCertificateFromStore(certContext);
    CertFreeCertificateContext(certContext);
    CryptDestroyKey(key);
    CryptReleaseContext(cryptProvider, 0);
    free(encodedSubject);
    free(pfx.pbData);

    return "";
}

// Now we don't need anything we had before because the PFX contains it all.
CertFreeCertificateContext(certContext);
CertCloseStore(initialTempStore, 0);
CryptDestroyKey(key);
CryptReleaseContext(cryptProvider, 0);
free(encodedSubject);

// Import the cert into a temporary store marking the keys as exportable.
HCERTSTORE tempStoreWithKeys = PFXImportCertStore(&pfx, password, CRYPT_EXPORTABLE | CRYPT_MACHINE_KEYSET);
if (tempStoreWithKeys == NULL) {
    _tprintf(_T("Unable to import PFX.\n"));
    PrintError((LPTSTR)_T("PFXImportCertStore"));

    free(encodedSubject);
    free(pfx.pbData);

    return "";
}

// Search through the temporary store to find the cert we want.
PCCERT_CONTEXT certWithPrivateKey = CertEnumCertificatesInStore(tempStoreWithKeys, nullptr);
if (certWithPrivateKey == NULL) {
    _tprintf(_T("Unable to enumerate temporary store. Error %d\n"), GetLastError());

    free(encodedSubject);
    free(pfx.pbData);

    return "";
}

while (certWithPrivateKey) {
    DWORD requiredSize = CertNameToStr(X509_ASN_ENCODING, &certWithPrivateKey->pCertInfo->Issuer, CERT_X500_NAME_STR, NULL, NULL);
    LPTSTR decodedSubject = (LPTSTR)malloc(requiredSize * sizeof(TCHAR));
    if (NULL == decodedSubject) {
        _tprintf(_T("malloc() failed: %d "), GetLastError());

        free(pfx.pbData);

        return "";
    }

    if (!CertNameToStr(X509_ASN_ENCODING, &certWithPrivateKey->pCertInfo->Issuer, CERT_X500_NAME_STR, decodedSubject, requiredSize)) {
        _tprintf(_T("Invalid certificate subject name. Error %d\n"), GetLastError());

        CertFreeCertificateContext(certWithPrivateKey);
        CertCloseStore(tempStoreWithKeys, 0);
        free(decodedSubject);
        free(pfx.pbData);

        return "";
    }

    if (_tcsncmp(subjectName, decodedSubject, requiredSize) != 0) {
        free(decodedSubject);
        certWithPrivateKey = CertEnumCertificatesInStore(tempStoreWithKeys, certWithPrivateKey);
        continue;
    }

    free(decodedSubject);
    break;
}

if (!certWithPrivateKey) {
    _tprintf(_T("No matching cert found in store\n."));

    CertFreeCertificateContext(certWithPrivateKey);
    CertCloseStore(tempStoreWithKeys, 0);
    free(pfx.pbData);

    return "";
}

Edit 3: After further discussions I've tried the following as well. After creating the temporary in-memory store and adding the certificate to it, I set the CERT_KEY_CONTEXT property on the resultant add operation as follows:

PCCERT_CONTEXT newCertContext;
// Add the certificate to the self-signed store.
if (!CertAddCertificateContextToStore(initialTempStore, certContext, CERT_STORE_ADD_REPLACE_EXISTING, &newCertContext)) {
    _tprintf(_T("Failed to add cert to local store."));

    CertCloseStore(initialTempStore, 0);
    CertFreeCertificateContext(certContext);
    CryptDestroyKey(key);
    CryptReleaseContext(cryptProvider, 0);
    free(encodedSubject);

    return "";
}

CERT_KEY_CONTEXT keyContext;
keyContext.cbSize = sizeof(CERT_KEY_CONTEXT);
keyContext.dwKeySpec = AT_SIGNATURE;
keyContext.hCryptProv = cryptProvider;
if (!CertSetCertificateContextProperty(newCertContext, CERT_KEY_CONTEXT_PROP_ID, 0, &keyContext)) {
    _tprintf(_T("Unable to set key context property on certificate context. Error %d\n"), GetLastError());

    CertFreeCertificateContext(certContext);
    CertFreeCertificateContext(newCertContext);
    CryptDestroyKey(key);
    CryptReleaseContext(cryptProvider, 0);
    CertCloseStore(initialTempStore, 0);
    free(encodedSubject);

    return "";
}

This does not resolve the issue either.

1

There are 1 best solutions below

9
RbMm On

private key never stored in certificate. it stored in crypto provider container. function CertSetCertificateContextProperty set property to certificate context (which is not equal to encoded certificate) and this is only link to crypto provider container. this link have no sense on another comp, because there not exist such container. and certificate context exist in memory only or stored in certificate store, which again not exist on another comp so we need store certificate + private key in PFX container. this can be done in next steps

  • Acquire Crypto Provider handle (CryptAcquireContextW). note that we can use in memory only context (use pszContainer = 0, dwFlags = CRYPT_VERIFYCONTEXT)
  • generates public/private key pair for container (CryptGenKey ), if it yet not exist. if we use in memory only container - need do this always
  • after certificate is created , create new in memory only store ( CertOpenStore(CERT_STORE_PROV_MEMORY)) and add certificate ( CertAddCertificateContextToStore ) to this store. this need only because PFXExportCertStoreEx accept only cert store as input but not standalone certificate
  • set Certificate Context Property to certificate in new store, not for original

*after this we can call PFXExportCertStoreEx for create PFX container*

this PFX can be moved already to another comps and installed here - during this process, new crypto container will be created, here will be stored private key from PFX, certificate will be put to some cert store and in this store will be link to container, where private key stored

minimal code example:

#define Get_Err(err, fn) err = (fn ? NOERROR : GetLastError())
#define No_Err(err, fn) NOERROR == (Get_Err(err, fn))
#define No_Err_V(err, q, p, fn) NOERROR == (err = ((p = fn) == q ? GetLastError() : NOERROR))

ULONG CreatePfx(PCWSTR szFileName,
              PCWSTR szPassword,
              PCWSTR SomeName = L"CN=SomeName", 
              ULONG dwKeySpec = AT_SIGNATURE,
              PCWSTR szProvider = MS_ENHANCED_PROV_W, 
              ULONG dwProvType = PROV_RSA_FULL)
{
    ULONG dwError;
    HCRYPTPROV hProv;

    if (No_Err(dwError, CryptAcquireContextW(&hProv, 0, szProvider, dwProvType, CRYPT_VERIFYCONTEXT)))
    {
        HCRYPTKEY hKey;
        if (No_Err(dwError, CryptGenKey(hProv, dwKeySpec, CRYPT_EXPORTABLE, &hKey)))
        {
            CryptDestroyKey(hKey);

            CRYPT_KEY_PROV_INFO KeyProvInfo = {
                0, const_cast<PWSTR>(szProvider), dwProvType, 0, 0, 0, dwKeySpec
            };

            CERT_NAME_BLOB cnb {};

            while (No_Err(dwError, CertStrToNameW(X509_ASN_ENCODING, SomeName, CERT_X500_NAME_STR, 0, cnb.pbData, &cnb.cbData, 0)))
            {
                if (cnb.pbData)
                {
                    PCCERT_CONTEXT pCertContext, pNewCertContext;

                    if (No_Err_V(dwError, NULL, pCertContext, CertCreateSelfSignCertificate(hProv, &cnb, 0, &KeyProvInfo,0, 0, 0, 0)))
                    {
                        HCERTSTORE hMemStore;

                        if (No_Err_V(dwError, NULL, hMemStore, CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, 0 )))
                        {
                            if (No_Err(dwError, CertAddCertificateContextToStore(hMemStore, pCertContext, CERT_STORE_ADD_NEW, &pNewCertContext)))
                            {
                                CERT_KEY_CONTEXT ckc = { sizeof(ckc), { hProv }, dwKeySpec };
                                CertSetCertificateContextProperty(pNewCertContext, CERT_KEY_CONTEXT_PROP_ID, 0, &ckc);
                                CertFreeCertificateContext(pNewCertContext);

                                DATA_BLOB db{};

                                while (No_Err(dwError, PFXExportCertStoreEx(hMemStore, &db, 
                                    szPassword, 0, EXPORT_PRIVATE_KEYS|
                                    REPORT_NO_PRIVATE_KEY|
                                    REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)))
                                {
                                    if (db.pbData)
                                    {
                                        // on remote comp call 
                                        // PFXImportCertStore(&db, szPassword, CRYPT_EXPORTABLE);
                                        HANDLE hFile;

                                        if (No_Err_V(dwError, INVALID_HANDLE_VALUE, hFile, CreateFileW(
                                            szFileName, FILE_APPEND_DATA, 0, 0, CREATE_ALWAYS, 0, 0)))
                                        {
                                            Get_Err(dwError, WriteFile(hFile, db.pbData, db.cbData, &db.cbData, 0));
                                            CloseHandle(hFile);
                                        }

                                        break;
                                    }

                                    db.pbData = (PBYTE)alloca(db.cbData);
                                }
                            }

                            CertCloseStore(hMemStore, 0);
                        }

                        CertFreeCertificateContext(pCertContext);
                    }
                    break;
                }
                cnb.pbData = (PUCHAR)alloca(cnb.cbData);
            }
        }

        CryptReleaseContext(hProv, 0);
    }

    return dwError;
}

another way:

  • export private key from crypto provider ( CryptExportKey(PRIVATEKEYBLOB) )
  • pack it with certificate
  • and send to remote comp
  • on remote comp create new crypto container
  • import private key ( CryptImportKey )
  • put certificate to the some store
  • bind certificate to private key container (CertSetCertificateContextProperty)

#define Get_Err(err, fn) err = (fn ? NOERROR : GetLastError())
#define No_Err(err, fn) NOERROR == (Get_Err(err, fn))
#define No_Err_V(err, q, p, fn) NOERROR == ((p = fn) == q ? GetLastError() : NOERROR)

ULONG ExportCertAndKey(_Out_ PDATA_BLOB pdb,
                 _Out_ PULONG pcbPrivateKey,
                 _In_ PCWSTR SomeName = L"CN=SomeName", 
                _In_ ULONG dwKeySpec = AT_KEYEXCHANGE,
                _In_ PCWSTR szProvider = MS_ENHANCED_PROV_W, 
                _In_ ULONG dwProvType = PROV_RSA_FULL)
{
    ULONG dwError;
    HCRYPTPROV hProv;

    if (No_Err(dwError, CryptAcquireContextW(&hProv, 0, szProvider, dwProvType, CRYPT_VERIFYCONTEXT)))
    {
        HCRYPTKEY hKey;
        if (No_Err(dwError, CryptGenKey(hProv, dwKeySpec, CRYPT_EXPORTABLE, &hKey)))
        {
            DATA_BLOB db{};
            while (No_Err(dwError, CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, db.pbData, &db.cbData)))
            {
                if (db.pbData)
                {
                    break;
                }

                db.pbData = (PBYTE)alloca(db.cbData);
            }

            CryptDestroyKey(hKey);

            if (NOERROR == dwError)
            {
                CRYPT_KEY_PROV_INFO KeyProvInfo = {
                    0, const_cast<PWSTR>(szProvider), dwProvType, 0, 0, 0, dwKeySpec
                };

                CERT_NAME_BLOB cnb {};

                while (No_Err(dwError, CertStrToNameW(X509_ASN_ENCODING, SomeName, CERT_X500_NAME_STR, 0, cnb.pbData, &cnb.cbData, 0)))
                {
                    if (cnb.pbData)
                    {
                        PCCERT_CONTEXT pCertContext;

                        if (No_Err_V(dwError, NULL, pCertContext, CertCreateSelfSignCertificate(hProv, &cnb, 0, &KeyProvInfo,0, 0, 0, 0)))
                        {
                            if (No_Err_V(dwError, NULL, pdb->pbData, new UCHAR[pdb->cbData = db.cbData + pCertContext->cbCertEncoded]))
                            {
                                *pcbPrivateKey = db.cbData;

                                memcpy(pdb->pbData, db.pbData, db.cbData);
                                memcpy(pdb->pbData + db.cbData, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded);
                            }

                            CertFreeCertificateContext(pCertContext);
                        }
                        break;
                    }
                    cnb.pbData = (PUCHAR)alloca(cnb.cbData);
                }
            }
        }

        CryptReleaseContext(hProv, 0);
    }

    return dwError;
}

ULONG ImportCertAndKey(_In_ PDATA_BLOB pdb, 
                       _In_ ULONG cbPrivateKey,
                       _In_ PCWSTR szContainer, 
                       _In_ ULONG dwKeySpec = AT_KEYEXCHANGE,
                       _In_ PCWSTR szProvider = MS_ENHANCED_PROV_W, 
                       _In_ ULONG dwProvType = PROV_RSA_FULL)
{
    ULONG dwError;
    HCRYPTPROV hProv;

    if (No_Err(dwError, CryptAcquireContextW(&hProv, szContainer, szProvider, dwProvType, CRYPT_NEWKEYSET)))
    {
        HCRYPTKEY hKey;
        if (No_Err(dwError, CryptImportKey(hProv, pdb->pbData, cbPrivateKey, 0, CRYPT_EXPORTABLE, &hKey)))
        {
            CryptDestroyKey(hKey);

            PCCERT_CONTEXT pCertContext, pNewCertContext;

            if (No_Err_V(dwError, NULL, pCertContext, CertCreateCertificateContext(
                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                pdb->pbData + cbPrivateKey, pdb->cbData - cbPrivateKey)))
            {
                HCERTSTORE hStore;

                if (No_Err_V(dwError, NULL, hStore, CertOpenSystemStoreW(0, L"MY")))
                {
                    if (No_Err(dwError, CertAddCertificateContextToStore(hStore, pCertContext, CERT_STORE_ADD_NEW, &pNewCertContext)))
                    {
                        CRYPT_KEY_PROV_INFO KeyProvInfo = {
                            const_cast<PWSTR>(szContainer), const_cast<PWSTR>(szProvider), dwProvType, 0, 0, 0, dwKeySpec
                        };

                        Get_Err(dwError, CertSetCertificateContextProperty(pNewCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &KeyProvInfo));

                        CertFreeCertificateContext(pNewCertContext);
                    }

                    CertCloseStore(hStore, 0);
                }

                CertFreeCertificateContext(pCertContext);
            }
        }

        CryptReleaseContext(hProv, 0);
    }

    return dwError;
}

    DATA_BLOB db;
    ULONG cbPrivKey;
    if (NOERROR == ExportCertAndKey(&db, &cbPrivKey))
    {
        ImportCertAndKey(&db, cbPrivKey, L"TestContainer");
        delete [] db.pbData;
    }