作爲我寫的應用程序的一部分,我希望我的程序在本地機器上臨時安裝證書,並且在連接到WinHTTP時使用它作爲客戶端證書一個Web服務器。這樣做的目的是幫助保護Web服務器免遭未經授權的訪問(這個證書只是一層安全性 - 我知道有人可以從.exe中提取它)。我不希望用戶必須安裝證書,並且我不希望證書在應用程序未運行時留在PC上。創建一個臨時客戶端證書(包括一個私鑰)
此刻,我想這樣的:
從.p12文件
使用C++應用程序來獲取二進制數據從本地證書,進入C數組手動安裝證書在我的應用程序(使用CryptExportKey和PCCERT_CONTEXT :: pbCertEncoded)
卸載證書
當應用程序啓動:
打開證書臨時存儲
m_certificateStoreHandle = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL);
呼叫CertAddEncodedCertificateToStore添加證書
CertAddEncodedCertificateToStore(m_certificateStoreHandle,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
reinterpret_cast< const BYTE * >(certificateData),
dataSize,
CERT_STORE_ADD_REPLACE_EXISTING,
&m_clientCertificate)
調用CryptAcquireContext要到什麼地方來存儲私鑰(我改變鍵的名稱每次運行就目前而言 - 理想我打算使用CRYPT_VERIFYCONTEXT使關鍵的非持續性,不過這事暫時忽略)
HCRYPTPROV cryptProvider = NULL;
HCRYPTKEY cryptKey = NULL;
CryptAcquireContext(&cryptProvider, "MyTestKeyNumber123", NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)
呼叫CryptImportKey私鑰加載到密鑰存儲
CryptImportKey(cryptProvider, reinterpret_cast< BYTE * >(privateKey), keySize, 0, CRYPT_EXPORTABLE, &cryptKey)
呼叫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))
驗證我可以訪問私鑰(工程,輸出完全一樣數據正如我硬編碼到應用程序)
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);
}
嘗試驗證客戶端證書的作品如預期
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);
}
在此階段,找到了私鑰,但對CryptExportKey的調用將失敗,並返回NTE_BAD_KEY_STATE。當我嘗試通過WinHTTP使用客戶端證書時,我得到ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEY。如果你想知道,我告訴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());
}
我看到它的方式,直到我能以某種方式連接的私鑰和證書一起,讓這個我可以使用使用CryptExportKey將CryptAcquireCertificatePrivateKey重新獲取密鑰數據,WinHTTP不會有成功的機會。
任何想法爲什麼我似乎無法讓我的證書使用私鑰?