添加到远程证书存储

添加到远程证书存储

本文介绍了通过 CryptAPI 生成并添加到远程证书存储的自签名证书没有私钥的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个场景,其中应用程序需要创建一个自签名证书并将其添加到远程计算机上的证书存储中.我已经尝试了 CryptAPI 和 CNG(尽管 CNG 似乎仍然使用 CryptAPI 来创建自签名证书并将其添加到远程证书存储中),但我看到的行为发生在两者中.

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.

环境:

同一域中的两台机器.一个是 Windows Server 2016 Standard,另一个是 Windows Server 2019 Datacenter.具有管理员权限的同一域用户用于登录两台计算机.在 2016 年的机器上运行应用程序,指明另一个机器的主机名.

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.

代码对 CString 使用 MFC/ATL,包括 wincrypt.h 和针对 crypt32.lib 的链接.针对 VS2019 的工具集和 VS2005 的工具集进行了测试.

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.

我所看到的:

自签名证书已创建并添加到远程存储中.如果我通过 MMC 查看证书,则表明它附有私钥.但是,当我尝试单击管理私钥..."时,会出现一个错误对话框:

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:

未找到证书密钥!"

同样,证书导出向导说:

Similarly the Certificate Export Wizard says:

"注意:找不到关联的私钥,只能导出证书."

并且是的,导出私钥"选项呈灰色显示.

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

这是我的代码的样子:

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

我发现了一个类似的问题 here 这就是我首先想到调用 CertSetCertificateContextProperty 的原因.但这并不能解决问题.

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.

我在这里遗漏了什么?

当打开的证书存储在本地机器上时,同样的代码也能工作.仅当证书存储在远程计算机上时才会出现此问题.

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.

编辑 2:在 RbMm 的建议下,我调查了将证书导出到 PFX 中.我不清楚将证书导出到 PFX 会如何改变任何事情,除了生成的 PFX 可能会做一些魔术以允许在我将其插入远程存储时传递密钥对.

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.

在那次调查中,我发现了这个 这帮助我更改了代码以使用 PFXExportCertStoreEx/PFXImportCertStore.您在下面看到的是我添加的内容(替换了 CertSetCertificateContextProperty 调用).但是,应该注意的是,这也不起作用.所以我又不知所措了.

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

编辑 3:经过进一步讨论后,我也尝试了以下方法.创建临时内存存储并向其添加证书后,我在结果添加操作上设置了 CERT_KEY_CONTEXT 属性,如下所示:

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

这也不能解决问题.

推荐答案

私钥从未存储在证书中.它存储在加密提供程序容器中.函数 CertSetCertificateContextProperty 将属性设置为证书上下文(不等于编码证书),这只是到加密提供程序容器的链接.这个链接在另一个组件上没有意义,因为不存在这样的容器.和证书上下文仅存在于内存中或存储在证书存储中,而在另一个组件中也不存在所以我们需要在 PFX 容器中存储 证书 + 私钥.这可以在接下来的步骤中完成

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 compso we need store certificate + private key in PFX container. this can be done in next steps

  • 获取加密提供程序句柄 (CryptAcquireContextW).请注意,我们只能在内存上下文中使用(使用 pszContainer = 0, dwFlags =CRYPT_VERIFYCONTEXT)
  • 为容器生成公钥/私钥对 (CryptGenKey),如果它还不存在.如果我们只在内存中使用容器 - 需要这样做总是
  • 证书创建后,只在内存中创建新的存储(CertOpenStore(CERT_STORE_PROV_MEMORY)) 并将证书 ( CertAddCertificateContextToStore ) 添加到此存储.这只是因为 PFXExportCertStoreEx 只接受证书存储作为输入,但是不是独立的证书
  • 将证书上下文属性设置为证书在新商店,而不是原创
  • Acquire Crypto Provider handle (CryptAcquireContextW). note that wecan use in memory only context (use pszContainer = 0, dwFlags =CRYPT_VERIFYCONTEXT)
  • generates public/private key pair for container (CryptGenKey ), if ityet not exist. if we use in memory only container - need do thisalways
  • 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 butnot standalone certificate
  • set Certificate Context Property to certificate in new store, notfor original

*之后我们可以调用PFXExportCertStoreEx来创建PFX容器*

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

这个 PFX 已经可以移动到另一个组件并安装在这里 - 在此过程中,将创建新的加密容器,这里将存储来自 PFX 的私钥,证书将被放置到某个证书存储中,并且在该存储中将链接到存储私钥的容器

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

最小代码示例:

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

  • 从加密提供者导出私钥(CryptExportKey(PRIVATEKEYBLOB) )
  • 用证书打包
  • 并发送到远程组件
  • 在远程组合上创建新的加密容器
  • 导入私钥 ( CryptImportKey )
  • 将证书放入某个商店
  • 将证书绑定到私钥容器(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;
    }

这篇关于通过 CryptAPI 生成并添加到远程证书存储的自签名证书没有私钥的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 23:42