[英]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.