Самоподписанный сертификат, созданный с помощью CryptAPI и добавленный в удаленное хранилище сертификатов, не имеет закрытого ключа. - PullRequest
0 голосов
/ 17 января 2020

У меня есть сценарий, в котором приложению необходимо создать самозаверяющий сертификат и добавить его в хранилище сертификатов на удаленном компьютере. Я пробовал и CryptAPI, и CNG (хотя CNG по-прежнему использует CryptAPI для создания самозаверяющего сертификата и добавления его в удаленное хранилище сертификатов), но поведение, которое я вижу, имеет место в обеих.

Среда:

Две машины в одном домене. Один из них - Windows Server 2016 Standard, а другой - Windows Server 2019 Datacenter. Один и тот же пользователь домена с правами администратора используется для входа на обе машины. Запустите приложение на компьютере 2016 года, указав имя другого хоста.

Код использует MFC / ATL для CString, включает wincrypt.h и ссылки на crypt32.lib. Проверено как на наборе инструментов VS2019, так и на наборе инструментов VS2005.

Что я вижу:

Самоподписанный сертификат создается и добавляется в удаленное хранилище. Если я просматриваю сертификат через MM C, это означает, что к нему прикреплен закрытый ключ. Однако, когда я пытаюсь нажать «Управление личными ключами ...», появляется сообщение об ошибке:

«Ключи для сертификата не найдены!»

Аналогично экспорт сертификата Мастер говорит:

«Примечание: связанный закрытый ключ не может быть найден. Только сертификат может быть экспортирован.»

и «Да, экспортировать закрытый ключ» опция неактивна.

Моя теория заключается в том, что закрытый ключ неправильно подключен к контексту сертификата (или не передается на удаленный компьютер, когда я добавляю ключ в хранилище сертификатов).

Вот как выглядит мой код:

#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;
}

Я обнаружил похожую проблему здесь , что и дало мне идею вызвать CertSetCertificateContextProperty. Но это не решает проблему.

Что мне здесь не хватает?

Редактировать: Этот же код работает, когда открытое хранилище сертификатов находится на локальном компьютере. Эта проблема возникает, только когда хранилище сертификатов находится на удаленной машине.

Редактировать 2: По предложению RbMm я исследовал экспорт сертификата в PFX. Мне было неясно, как экспорт сертификатов в PFX изменит что-либо, за исключением того, что, возможно, сгенерированный PFX сделает несколько магий c, чтобы разрешить передачу пары ключей, когда я вставлю ее в удаленное хранилище.

В этом исследовании я обнаружил этот , который помог мне изменить мой код для использования PFXExportCertStoreEx / PFXImportCertStore. Ниже вы видите то, что я добавил (заменив CertSetCertificateContextProperty вызов). Однако следует отметить, что это тоже не сработало . Так что я снова в растерянности.

// 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 "";
}

Правка 3: После дальнейших обсуждений я также попробовал следующее. После создания временного хранилища в памяти и добавления в него сертификата я установил свойство CERT_KEY_CONTEXT для результирующей операции добавления следующим образом:

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 "";
}

Это также не решает проблему.

1 Ответ

1 голос
/ 18 января 2020

закрытый ключ никогда не хранится в сертификате. он хранится в контейнере криптографического провайдера. Функция CertSetCertificateContextProperty устанавливает свойство в контекст сертификата (который не равен закодированному сертификату), и это только ссылка на контейнер поставщика криптографии. эта ссылка не имеет смысла на другом компе, потому что такого контейнера не существует. и контекст сертификата существует только в памяти или хранится в хранилище сертификатов, которое снова не существует в другом компе, поэтому нам нужно хранить сертификат + закрытый ключ в PFX контейнере. это можно сделать в следующих шагах

  • Получить дескриптор Crypto Provider ( CryptAcquireContextW ). обратите внимание, что мы можем использовать только контекст памяти (используйте pszContainer = 0, dwFlags = CRYPT_VERIFYCONTEXT )
  • генерирует пару открытого / закрытого ключа для контейнера ( CryptGenKey ), если это еще не существует. если мы используем контейнер только для памяти - нужно делать это всегда
  • после создания сертификата, создать новое хранилище только в памяти ( CertOpenStore (CERT_STORE_PROV_MEMORY) ) и добавить сертификат ( CertAddCertificateContextToStore ) в этот магазин. это необходимо только потому, что PFXExportCertStoreEx принимает в качестве входных данных только хранилище сертификатов, но не автономный сертификат
  • устанавливает свойство контекста сертификата для сертификата в новом хранилище , а не для исходного

* после этого мы можем вызвать PFXExportCertStoreEx для создания контейнера PFX *

этот PFX может быть уже перемещен в другой комп и установлен здесь - во время этого процесс, будет создан новый крипто-контейнер, здесь будет храниться закрытый ключ от PFX , сертификат будет помещен в некоторое хранилище сертификатов, и в этом хранилище будет ссылка на контейнер, где хранится закрытый ключ

пример минимального кода:

#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;
}

другой способ:

  • экспорт закрытого ключа от поставщика шифрования ( CryptExportKey (PRIVATEKEYBLOB)) *
  • упакуйте его с сертификатом
  • и отправьте удаленному компу
  • на удаленном компе, создайте новый крипто-контейнер
  • импортируйте закрытый ключ ( CryptImportK эй )
  • положить сертификат в некоторый магазин
  • привязать сертификат к контейнеру закрытого ключа ( 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;
    }
...