Создание временного сертификата клиента (включая закрытый ключ) - PullRequest
2 голосов
/ 01 сентября 2011

Как часть приложения, которое я пишу, я хочу, чтобы моя программа временно установила сертификат на локальном компьютере, а WinHTTP использовал его в качестве сертификата клиента при подключении к веб-серверу. Цель этого состоит в том, чтобы помочь защитить веб-сервер от несанкционированного доступа (этот сертификат является лишь слоем безопасности - я знаю, что кто-то может извлечь его из .exe). Я не хочу, чтобы пользователь устанавливал сертификат, и не хочу, чтобы сертификат оставался на ПК, когда приложение не запущено.

На данный момент я пытаюсь это:

Установить сертификат вручную из файла .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() );
        }

На мой взгляд, пока я не смогу каким-то образом связать закрытый ключ и сертификат и сделать так, чтобы я мог использовать CryptAcquireCertificatePrivateKey с CryptExportKey для возврата данных ключа, WinHTTP не имеет шансов на успех ,

Есть мысли, почему я не могу получить свой сертификат для использования закрытого ключа?

1 Ответ

1 голос
/ 02 сентября 2011

Мне не удалось заставить этот подход работать.Вместо этого я использовал 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 );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...