2011-09-01 2 views
2

필자가 작성한 응용 프로그램의 일부로 내 프로그램이 로컬 컴퓨터에 인증서를 임시로 설치하고 연결할 때 WinHTTP에서 클라이언트 인증서로 사용하기를 바랍니다. 웹 서버. 이 목적은 무단 액세스로부터 웹 서버를 보호하는 것입니다 (이 인증서는 보안 계층 ​​일 뿐이며 누군가가 .exe에서 압축을 풀 수 있음을 알고 있습니다). 사용자가 인증서를 설치하지 않아도되고 응용 프로그램이 실행되고 있지 않을 때 인증서를 PC에 남기지 않으려합니다. 순간 임시 클라이언트 인증서 (개인 키 포함) 만들기

, 나는이 노력하고있어 :

은 .P12 파일

사용은 C++ 응용 프로그램 로컬 인증서에서와 C 배열에 바이너리 데이터를 얻을 수에서 인증서를 수동으로 설치 내 응용 프로그램에

제거 인증서

(CryptExportKey 및 PCCERT_CONTEXT :: pbCertEncoded 사용) 할 때 응용 프로그램 부츠 :

,363,210

열기 인증서에 대한 임시 저장소에 개인 키를 저장하는 곳 얻을 수있는 인증서를

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

통화 CryptAcquireContext를 추가 할 수

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

통화 CertAddEncodedCertificateToStore (I 각 실행에 키의 이름을 변경 순간 - 이상적으로 나는 키 비에 영구를 만들기 위해 CRYPT_VERIFYCONTEXT를 사용하려면,하지만 지금은 무시하는 일)

HCRYPTPROV cryptProvider = NULL; 
HCRYPTKEY cryptKey = NULL; 
CryptAcquireContext(&cryptProvider, "MyTestKeyNumber123", NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET) 
입니다전화 CryptImportKey 내가 개인 키에 액세스 할 수 있는지 확인 (개인 키

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

에 작품을 인증서를 연결하는 키 저장소에

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

전화 CertSetCertificateContextProperty를 개인 키를로드, 정확히 같은 출력 내가) 응용 프로그램에 클라이언트 인증서의 작동을 확인하는

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

이 단계에서는 개인 키가 발견되었지만 NTE_BAD_KEY_STATE를 사용하면 CryptExportKey에 대한 호출이 실패합니다. WinHTTP에서 클라이언트 인증서를 사용하려고하면 ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEY가 표시됩니다. 내가 함께 개인 키와 인증서를 연결 어떻게 든 할 수있을 때까지,

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()); 
     } 

나는 그것을 볼 수있는 방법을 내가 사용할 수 있도록합니다 사람이 궁금해하는 경우,이 코드와 클라이언트 인증서를 사용하여 WinHTTP에게 CryptExcryptionKey를 사용하여 키 데이터를 다시 가져 오는 CryptAcquireCertificatePrivateKey는 WinHTTP가 성공할 기회를 제공하지 않습니다.

내 인증서가 개인 키를 사용하지 못하는 이유에 대한 의견이 있으십니까?

답변

1

나는이 접근법을 작동시키지 못했습니다.대신 PFXImportCertStore를 인증서 및 개인 키가 포함 된 원시 .p12 파일과 함께 사용했습니다.

내가 본 바로는 PFXImportCertStore가 메모리에 새 저장소를 만드는 것으로 보입니다. 개인 키가 메모리에 저장되어 있거나 PC에서 영구적으로 종료되는 경우에만 찾을 수있는 유일한 방법입니다. 두 가지 중 하나를 발견하면이 대답을 업데이트 할 것입니다.

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