简体   繁体   English

创建临时客户端证书(包括私钥)

[英]Creating a temporary client certificate (including a private key)

As part of an application I am writing, I wish for my program to temporarily install a certificate on the local machine and for WinHTTP to use it as the client certificate when connecting to a web server. 作为我正在编写的应用程序的一部分,我希望程序可以在本地计算机上临时安装证书,并希望WinHTTP在连接到Web服务器时将其用作客户端证书。 The aim of this is help protect the web server from unauthorised access (this certificate is only a layer of the security - I know someone could extract it from the .exe). 这样做的目的是帮助保护Web服务器免遭未经授权的访问(此证书只是安全性的一层-我知道有人可以从.exe提取它)。 I do not want the user to have to install the certificate and I do not want the certificate to be left on the PC when the application is not running. 我不希望用户必须安装证书,也不希望在应用程序未运行时将证书留在PC上。

At the moment, I'm trying this: 目前,我正在尝试:

Install the certificate manually from a .p12 file 从.p12文件手动安装证书

Use a C++ application to get the binary data out of the local certificate and into a C array in my application (using CryptExportKey and PCCERT_CONTEXT::pbCertEncoded) 使用C ++应用程序将二进制数据从本地证书中取出,并放入我的应用程序中的C数组中(使用CryptExportKey和PCCERT_CONTEXT :: pbCertEncoded)

Uninstall the certificate 卸载证书

When the application boots: 应用程序启动时:

Open a temporary store for the certificate 打开证书的临时存储区

m_certificateStoreHandle = CertOpenStore( CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL );

Call CertAddEncodedCertificateToStore to add the certificate 调用CertAddEncodedCertificateToStore添加证书

CertAddEncodedCertificateToStore( m_certificateStoreHandle,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 
reinterpret_cast< const BYTE * >( certificateData ),
dataSize, 
CERT_STORE_ADD_REPLACE_EXISTING,
&m_clientCertificate )

Call CryptAcquireContext to get somewhere to store the private key (I change the name of the key on each run for the moment - ideally I plan to use CRYPT_VERIFYCONTEXT to make the key non-persistant, but that's something to ignore for now) 调用CryptAcquireContext到某个地方存储私钥(我现在每次运行都会更改密钥的名称-理想情况下,我计划使用CRYPT_VERIFYCONTEXT来使密钥变为非持久性,但现在暂时可以忽略)

HCRYPTPROV cryptProvider = NULL;
HCRYPTKEY cryptKey = NULL;
CryptAcquireContext( &cryptProvider, "MyTestKeyNumber123", NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET )

Call CryptImportKey to load the private key into the key store 调用CryptImportKey将私钥加载到密钥存储区中

CryptImportKey( cryptProvider, reinterpret_cast< BYTE * >( privateKey ), keySize, 0, CRYPT_EXPORTABLE, &cryptKey )

Call CertSetCertificateContextProperty to link the certificate to the private key 调用CertSetCertificateContextProperty将证书链接到私钥

char containerName[128];
DWORD containerNameSize = ARRAY_NUM_BYTES(containerName);
char providerName[128];
DWORD providerNameSize = ARRAY_NUM_BYTES(providerName);

CryptGetProvParam(cryptProvider, PP_CONTAINER, reinterpret_cast<byte *>(containerName), &containerNameSize, 0)
CryptGetProvParam(cryptProvider, PP_NAME, reinterpret_cast<byte *>(providerName), &providerNameSize, 0)

WCHAR containerNameWide[128];
convertCharToWChar(containerNameWide, containerName);
WCHAR providerNameWide[128];
convertCharToWChar(providerNameWide, providerName);

CRYPT_KEY_PROV_INFO privateKeyData;
neMemZero(&privateKeyData, sizeof(privateKeyData));
privateKeyData.pwszContainerName = containerNameWide;
privateKeyData.pwszProvName = providerNameWide;
privateKeyData.dwProvType = 0;
privateKeyData.dwFlags = CRYPT_SILENT;
privateKeyData.dwKeySpec = AT_KEYEXCHANGE;

if ( CertSetCertificateContextProperty( m_clientCertificate, CERT_KEY_PROV_INFO_PROP_ID, 0, &privateKeyData ) )

Verify I can access the private key (Works, outputs exactly the same data as I have hardcoded into the application) 验证我可以访问私钥(Works,输出与我硬编码到应用程序中的数据完全相同的数据)

byte privateKeyBuffer[2048];
DWORD privateKeyBufferSize = ARRAY_NUM_BYTES(privateKeyBuffer);
memZero(privateKeyBuffer, privateKeyBufferSize);
if(CryptExportKey(cryptKey, 0, PRIVATEKEYBLOB, 0, privateKeyBuffer, &privateKeyBufferSize))
{
   TRACE("Got private key!");
   LOG_BUFFER(privateKeyBuffer, privateKeyBufferSize);
}

Attempt to verify the client certificate works as expected 尝试验证客户端证书是否按预期工作

char certNameBuffer[128] = "";
char certUrlBuffer[128] = "";
CertGetNameString(testValue, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, certNameBuffer, ARRAY_NUM_BYTES(certNameBuffer));
CertGetNameString(testValue, CERT_NAME_URL_TYPE , 0, NULL, certUrlBuffer, ARRAY_NUM_BYTES(certUrlBuffer));
TRACE("SSL Certificate %s [%s]", certNameBuffer, certUrlBuffer);

HCRYPTPROV_OR_NCRYPT_KEY_HANDLE privateKey;
DWORD privateKeyType;
BOOL freeKeyAfter = false;
if(CryptAcquireCertificatePrivateKey(testValue, CRYPT_ACQUIRE_NO_HEALING, NULL, &privateKey, &privateKeyType, &freeKeyAfter))
{
    HCRYPTPROV privateKeyProvider = static_cast<HCRYPTPROV>(privateKey);
    HCRYPTKEY privateKeyHandle;
    if(CryptGetUserKey(privateKeyProvider, privateKeyType, &privateKeyHandle))
    {
        NEbyte privateKeyBuffer[2048];
        DWORD privateKeyBufferSize = NE_ARRAY_NUM_BYTES(privateKeyBuffer);
        neMemZero(privateKeyBuffer, privateKeyBufferSize);
        if(CryptExportKey(privateKeyHandle, 0, PRIVATEKEYBLOB, 0, privateKeyBuffer, &privateKeyBufferSize))
        {
            NE_TRACE("Got private key!");
            HTTP_LOG_BUFFER(neGetGlobalTraceLog(), "Key", "", privateKeyBuffer, privateKeyBufferSize);
        }

At this stage, the private key is found but the call to CryptExportKey fails with NTE_BAD_KEY_STATE. 在此阶段,找到了私钥,但对CryptExportKey的调用失败,并显示NTE_BAD_KEY_STATE。 When I try to use the client certificate with WinHTTP, I get ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEY. 当我尝试将客户端证书与WinHTTP一起使用时,出现ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEY。 If anyone is wondering, I tell WinHTTP to use the client certificate with this code: 如果有人想知道,我告诉WinHTTP使用带有以下代码的客户端证书:

if ( !WinHttpSetOption( handle,
                                WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
                                const_cast<PCERT_CONTEXT>(m_clientCertificate),
                                sizeof( CERT_CONTEXT ) ) )
        {
            HTTP_LOG_ERROR( getLog(), "Setting the client certificate failed with error code %x", GetLastError() );
        }

The way I see it, until I can somehow link the private key and the certificate together and make it so that I can use CryptAcquireCertificatePrivateKey with CryptExportKey to get the key data back out, WinHTTP doesn't stand a chance of being succesful. 我所看到的方式,直到我能以某种方式将私钥和证书链接在一起,然后才能使用CryptAcquireCertificatePrivateKey和CryptExportKey来获取密钥数据为止,WinHTTP不会成功。

Any thoughts as to why I can't seem to get my certificate to use the private key? 关于为什么我似乎无法获得使用私钥的证书的任何想法?

I didn't manage to get this approach working. 我没有设法使这种方法起作用。 Instead, I ended up using PFXImportCertStore along with the raw .p12 file containing the certificate and private key I wanted to use. 相反,我最终使用了PFXImportCertStore以及包含要使用的证书和私钥的原始.p12文件。

From what I've seen, it seems that PFXImportCertStore will create a new store in memory. 从我所看到的,看来PFXImportCertStore将在内存中创建一个新存储。 The only thing I haven't been able to find out is if the private key is also stored in memory or if it ends up permenently on the PC somewhere. 我唯一无法发现的是私钥是否也存储在内存中,或者是否永久存在于PC上的某个地方。 If I find out either way, I'll update this answer. 如果我找到任何一种方法,我都会更新此答案。

m_clientCertificateStoreHandle = PFXImportCertStore(&pfxData, certificatePassword, 0);
if(NULL != m_clientCertificateStoreHandle)
{
m_clientCertificateHandle = CertFindCertificateInStore( m_clientCertificateStoreHandle, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM