2011-09-01 79 views
2

作爲我寫的應用程序的一部分,我希望我的程序在本地機器上臨時安裝證書,並且在連接到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不會有成功的機會。

任何想法爲什麼我似乎無法讓我的證書使用私鑰?

回答

1

我沒有設法讓這種方法奏效。相反,我最終使用PFXImportCertStore以及包含我想要使用的證書和私鑰的原始.p12文件。

從我所看到的看來,PFXImportCertStore似乎會在內存中創建一個新的商店。我唯一無法發現的是私鑰是否也存儲在內存中,或者如果它最終在個人電腦的某處發生了滲透。如果我發現任何一種方式,我會更新這個答案。

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); 
} 
相關問題