Развернуть все
Свернуть все

Проверка использования для подписи OCSP-ответов

Пример проверки цепочки и использования конечного сертификата для выполнения операций подписи OCSP-ответов с использованием флага CPCERT_CHAIN_POLICY_OCSP_SIGNING в функции CertVerifyCertificateChainPolicy.

C++
    // Параметры для CertVerifyCertificateChainPolicy. 
    CERT_CHAIN_POLICY_PARA para = { sizeof(para) };

    // Помимо флагов, разрешенных в CERT_CHAIN_POLICY_PARA,
    // можно потребовать не проверять конечный сертификат, установив флаг
    // CPPRIVATEKEY_USAGE_PERIOD_CERT_CHAIN_POLICY_SKIP_END_CERT_FLAG,
    // тогда для проверки передавать время проверки в pvExtraPolicyPara 
    // нет необходимости. Будет проверена корректность построения самой
    // цепочки: закрытые ключи соответствующих сертификатов цепочки 
    // (исключая конечный) были использованы для подписи объектов PKI 
    // таких как CRL, CTL, выпущенный конечный сертификат или 
    // промежуточного ЦС, если это сертификат ЦС.  
    //
    // Значение HIWORD para.dwFlags должно быть незаполнено.
    // 
    // Выставляем необходимые флаги
    para.dwFlags = dwFlags; 

    // Указываем дополнительные параметры с указанием времени проверки 
    // срока действия закрытого ключа конечного сертификата.
    //
    // Эту структуру можно не передавать, если необходимо проверить 
    // допустимость использования закрытого ключа в 
    // данный момент времени, или если указан флаг SKIP_END_CERT.  
    CPOCSP_SIGNING_EXTRA_CERT_CHAIN_POLICY_PARA extraPara
        = { sizeof(extraPara) };    
    extraPara.pPrivateKeyUsedTime = &eeCertVerifyTime;
    // Задаем массив идентификаторов сертификатов
    extraPara.cCertId = static_cast<DWORD>(rgCertId.size());
    extraPara.rgCertId = &rgCertId[0];

    para.pvExtraPolicyPara = &extraPara;    


    // Готовим структуру для получения дополнительного статуса 
    // проверки цепочки. В ней будут указаны только специфичные коды
    // ошибок для расширенной проверки. Стандартные коды ошибок 
    // проверки цепочки возвращаются в status.dwError. Если pvExtraPolicyStatus
    // не задан, проверка завершается после первой ошибкb при проверке 
    // и в status.dwError возвращаются только коды ошибок, определенных в 
    // заголовочных файлах Microsoft.
    CERT_CHAIN_POLICY_STATUS status = { sizeof(status) };
    CPOCSP_SIGNING_EXTRA_CERT_CHAIN_POLICY_STATUS extraStatus
        = { sizeof(extraStatus) };
    
    // Задаём адрес массива статусов по отдельным запросам. 
    extraStatus.rgCertIdStatus = &srAuthStatus[0];

    status.pvExtraPolicyStatus = &extraStatus;

    // Зовём CertVerifyCertificateChainPolicy
    if(!::CertVerifyCertificateChainPolicy(
        CPCERT_CHAIN_POLICY_OCSP_SIGNING, 
        pChainContext, 
        &para,
        &status)) {
            // "Цепочка не была проверена" 
            return HRESULT_FROM_WIN32(GetLastError());
    }

    // Произошла ошибка при проверке по флагу 
    // CPCERT_CHAIN_POLICY_SIGNATURE
    // Если задана pvExtraPolicyStatus, то 
    // можно получить больше информации об ошибке
    if(status.dwError) {
        hr = HRESULT_FROM_WIN32(status.dwError);
        DoSmthIfError(L"Общая ошибка проверки цепочки:" + GetErrorMessage(hr)); 
    }

    // Ошибка произошла при проверке периода действия закрытого ключа 
    if (extraStatus.dwError & 
        (CPCERT_TRUST_PRIVATE_KEY_IS_NOT_TIME_VALID 
        | CPCERT_TRUST_PRIVATE_KEY_IS_NOT_TIME_VALID_FOR_CRL))
    {
        // Разбор детальной причины ошибки см. в примере 
        // проверки периода действия закрытого ключа 
        DoSmthIfError(L"Один из сертификатов цепочки "
            L"был использован для подписи " 
            L"вне периода действия закрытого ключа");
        if (SUCCEEDED(hr))
            hr = CERT_E_EXPIRED;
    }

    // Ошибка произошла при проверке конечного сертификата
    if (extraStatus.dwError & 
        CPCERT_TRUST_IS_NOT_VALID_BY_KEYUSAGE)
    {
        DoSmthIfError(L"В конечном сертификате отсутствуют флаги " 
            L"CERT_NON_REPUDIATION_KEY_USAGE или CERT_DIGITAL_SIGNATURE_KEY_USAGE");
        if (SUCCEEDED(hr))
            hr = CERT_E_WRONG_USAGE;
    }

    // Сертификат признан недопустимым для подписи OCSP-ответов
    if (extraStatus.dwError & 
        CPCERT_TRUST_IS_NOT_VALID_FOR_USAGE)
    {
        DoSmthIfError(L"Сертификат был признан недопустимым для использования");
        if (SUCCEEDED(hr))
            hr = CERT_E_WRONG_USAGE;
    }   
    
    // проверяем статусы переданных certID
    for(unsigned int i = 0; i < srAuthStatus.size(); ++i) 
    {
        if (!extraStatus.rgCertIdStatus[i])
        {
            DoSmthIfError(L"Статус сертификата не может быть подписан"
                L"на проверяемом сертификате");
        }
    }

    return hr;