Вручную проверить сертификат сервера в WinINet - PullRequest
2 голосов
/ 09 апреля 2009

Я пытаюсь реализовать ручную самозаверяющую проверку сертификата SSL для клиента WinINet. Я попытался подойти к нему, вызвав InternetQueryOption с параметрами INTERNET_OPTION_SECURITY_CERTIFICATE или INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, но оба возвращают некоторую внутреннюю интерпретацию сертификата сервера, ни один из которых не позволяет получить доступ к необработанному открытому ключу сертификата или, по крайней мере, к thumbprimnt.

Как мне проверить сертификат? ...

Ответы [ 2 ]

2 голосов
/ 24 августа 2016

Дополнительно к предыдущему ответу. Если вы хотите вручную проверять сертификаты с ненадежным корнем (например, с собственной подписью), вам нужно

1) Установить флаги, чтобы игнорировать недоверенные сертификаты

// Open request before
HINTERNET hRequest = HttpOpenRequest(hConnect, _T("POST"), action, NULL, NULL, NULL, dwFlags, 0);
if (!hRequest) return GetLastError();

// set ignore options to request
DWORD dwFlags;
DWORD dwBuffLen = sizeof(dwFlags);
InternetQueryOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, (LPVOID)&dwFlags, &dwBuffLen);
dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
InternetSetOption (hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags));

2) Отправить запрос данных

If(HttpSendRequest(hRequest, strHeaders, strHeaders.GetLength(), data, len)) {

3) И только после возврата запроса данных мы можем просмотреть информацию о сертификате и проверить отпечаток. Чтобы получить отпечаток, мы должны использовать методы из cryptapi, поэтому необходимо #include "WinCrypt.h" и добавить crypt32.lib в компоновщик

PCCERT_CHAIN_CONTEXT CertCtx=NULL;
DWORD cbCertSize = sizeof(&CertCtx);

// Get certificate chain information
if (InternetQueryOption(hRequest, INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT, (LPVOID)&CertCtx, &cbCertSize))
{
    PCCERT_CHAIN_CONTEXT pChainContext=CertCtx;

    CERT_SIMPLE_CHAIN *simpleCertificateChainWithinContext = NULL;
    for (int i=0; i<pChainContext->cChain; i++)
    {
        simpleCertificateChainWithinContext=pChainContext->rgpChain[i];

        // We can check any certificates from chain, but if selfsigned it will be single
        for (int simpleCertChainIndex = 0; simpleCertChainIndex < simpleCertificateChainWithinContext->cElement; impleCertChainIndex++)
        {
            // get the CertContext from the array
            PCCERT_CONTEXT pCertContext = simpleCertificateChainWithinContext->rgpElement[simpleCertChainIndex]->pCertContext;

            // Public key can be getted from
            //  (((*((*pCertContext).pCertInfo)).SubjectPublicKeyInfo).PublicKey).pbData
            // but better to use thumbprint to check

            // CERT_HASH_PROP_ID - is a thumbprint
            BYTE thumbprint[1024];
            DWORD len = 1024;
            if(CertGetCertificateContextProperty(pCertContext, CERT_HASH_PROP_ID, thumbprint, &len)) {
                //
                // !!! HERE WE CAN CHECK THUMPRINT WITH TRUSTED(PREVIOUSLY SAVED)
                // and return error, or accept request output.
                //
            }
        }
    }

    // important! Free the CertCtx
    CertFreeCertificateChain(CertCtx);
}

4) Зачем использовать отпечаток пальца для сравнения сертификата? В сертификате есть три интересных поля для сравнения: Серийный номер, Открытый ключ, Отпечаток пальца. Серийный номер - это просто уникальный номер, выбранный центром сертификации, выдавшим сертификат, открытый ключ хорошее решение, но отпечаток большого пальца вычисляется по хешу по всему сертификату (https://security.stackexchange.com/questions/35691/what-is-the-difference-between-serial-number-and-thumbprint)

0 голосов
/ 01 августа 2009

WinInet уже проверит, что возвращенное доменное имя сертификата соответствует сертификату, и цепочка сертификатов является доверенной, если вы установили INTERNET_FLAG_SECURE при вызове HttpOpenRequest.

Несколько вещей, которые вы можете сделать потом:

  1. Используйте INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT в lpszIssuerInfo для сравнения возвращенного имени домена и совпадения имени сертификата с ожидаемым родительским сертификатом.

  2. Разобрать имя эмитента из lpszIssuerInfo и вызвать CertFindCertificateInStore для получения указателя контекста сертификата.

  3. Получение и проверка цепочки сертификатов с использованием CertGetCertificateChain и указателя контекста сертификата, например сравнения отпечатков пальцев выдачи сертификатов, но не самого сертификата, насколько мне известно.

Для дальнейшего использования из MSDN: "http://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx". Если установлен IE8.0, существует новая опция, которая раскрывает цепочку сертификатов сервера.

INTERNET_OPTION_SERVER_CERT_CHAIN_CONTEXT 105

Извлекает контекст цепочки сертификатов сервера в виде дублированного PCCERT_CHAIN_CONTEXT. Вы можете передать этот дублированный контекст любой функции Crypto API, которая принимает PCCERT_CHAIN_CONTEXT. Вы должны вызвать CertFreeCertificateChain для возвращенного PCCERT_CHAIN_CONTEXT, когда закончите с контекстом цепочки сертификатов.

Версия: требуется Internet Explorer 8.0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...