Пример проверки цепочки сертификатов на корректность использования закрытого ключа сертификата для подписи выпущенного сертификата, базового или дельта CRL, содержащегося в цепочке, подписи CTL с использованием флага CPCERT_CHAIN_POLICY_PRIVATEKEY_USAGE_PERIOD в функции CertVerifyCertificateChainPolicy.

C++ Copy Code
    // Параметры для 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, выпущенный конечный сертификат или 
    // промежуточного ЦС, если это сертификат ЦС.    
    // 
    // Выставляем необходимые флаги
    para.dwFlags = dwFlags; 

    // Указываем дополнительные параметры с указанием времени проверки 
    // срока действия закрытого ключа конечного сертификата.
    //
    // Эту структуру можно не передавать, если необходимо проверить 
    // допустимость использования закрытого ключа в 
    // данный момент времени, или если указан флаг SKIP_END_CERT.  
    CPPRIVATEKEY_USAGE_PERIOD_EXTRA_CERT_CHAIN_POLICY_PARA pkupExtPara
        = { sizeof(pkupExtPara) };    
    pkupExtPara.pPrivateKeyUsedTime = &eeCertVerifyTime;
    para.pvExtraPolicyPara = &pkupExtPara;


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

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

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

    // Ошибка произошла при проверке сертификата 
    if (pkupExtStatus.dwError & CPCERT_TRUST_PRIVATE_KEY_IS_NOT_TIME_VALID)
    {
        // Показываем детальную информацию об ошибке

        // Закрытый ключ конечного сертификата не может использоваться
        // на указанное время проверки
        if ( pkupExtStatus.lChainIndex == 0 &&
            pkupExtStatus.lElementIndex == 0)
        {
            // Сообщаем об ошибке
            DoSmthIfError(L"Конечный сертификат нельзя использовать на момент проверки"); 
            if (SUCCEEDED(hr))
                hr = CERT_E_EXPIRED;
        }

        // CTL был подписан сертификатом, закрытый ключ 
        // которого не мог использоваться в момент подписи CTL
        else if ( pkupExtStatus.lChainIndex > 0 &&
            pkupExtStatus.lElementIndex == 0)
        {               
            // Сообщаем об ошибке и можем дополнительно 
            // показать пользователю недопустимый сертификат
            DoSmthIfError(L"Сертификат был использован для подписи "
                L"CTL вне периода действия закрытого ключа",
                pChainContext->rgpChain[pkupExtStatus.lChainIndex]->
                    rgpElement[pkupExtStatus.lElementIndex]->pCertContext);
            if (SUCCEEDED(hr))
                hr = CERT_E_EXPIRED;
        }
                    
        // ЦА выпустил сертификат либо подчиненного ЦА (pkupExtStatus.lElementIndex > 1)
        // либо конечный сответствующий конечный сертификат (pkupExtStatus.lElementIndex == 1)
        // вне периода действия своего закрытого ключа
        else if ( pkupExtStatus.lChainIndex >=0 &&
             pkupExtStatus.lElementIndex >= 0)
        {
            // Сообщаем об ошибке и можем дополнительно 
            // показать пользователю недопустимый сертификат
            DoSmthIfError(L"Сертификат был использован для подписи "
                L"CTL вне периода действия закрытого ключа",
                pChainContext->rgpChain[pkupExtStatus.lChainIndex]->
                rgpElement[pkupExtStatus.lElementIndex]->pCertContext);
            if (SUCCEEDED(hr))
                hr = CERT_E_EXPIRED;
        }
    }

    // Ошибка произошла при проверке сертификата 
    if (pkupExtStatus.dwError & CPCERT_TRUST_PRIVATE_KEY_IS_NOT_TIME_VALID_FOR_CRL)
    {
        // Показываем детальную информацию об ошибке

        // Закрытый ключ сертификата не должен был использоваться
        // для выпуска соответствующего CRL, лежащего в цепочке
        if ( pkupExtStatus.lChainIndex >= 0 &&
            pkupExtStatus.lElementIndex >= 0)
        {
            // Сообщаем об ошибке
            DoSmthIfError(L"Сертификат был использован для подписи "
                L"CRL вне периода действия закрытого ключа",
                pChainContext->rgpChain[pkupExtStatus.lChainIndex]->
                rgpElement[pkupExtStatus.lElementIndex]->pCertContext);
            if (SUCCEEDED(hr))
                hr = CERT_E_EXPIRED;
        }
        else
        {
            DoSmthIfError(L"Один из сертификатов цепочки "
                L"был использован для подписи CRL " 
                L"вне периода действия закрытого ключа");
            if (SUCCEEDED(hr))
                hr = CERT_E_EXPIRED;
        }
    }

    return hr;