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

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

Пример проверки цепочки и использования конечного сертификата для выполнения операций подписи OCSP-ответов с использованием флага CPCERT_CHAIN_POLICY_TIMESTAMP_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, выпущенный конечный сертификат или 
    // промежуточного ЦС, если это сертификат ЦС.  
    // Также можно использовать флаги 
    // CPTIMESTAMP_SIGNING_CERT_CHAIN_POLICY_IGNORE_NOT_ONE_EKU_FLAG
    // (игнорировать использование сертификата не только для подписи штампов времени)
    // и
    // CPTIMESTAMP_SIGNING_CERT_CHAIN_POLICY_IGNORE_NOT_CRITICAL_EKU_FLAG
    // (игнорировать если расширение улучшенный ключ - некритическое)
    // Проверять, является ли конечный сертификат сертификатом ЦА, нельзя.
    // Сертификат должен быть END_ENTITY. В HIWORD para.dwFlags использовать
    // другие флаги, кроме упомянутых выше - нельзя.
    // 
    // Выставляем необходимые флаги
    para.dwFlags = dwFlags; 

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


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

    // Зовём CertVerifyCertificateChainPolicy
    if(!::CertVerifyCertificateChainPolicy(
        CPCERT_CHAIN_POLICY_TIMESTAMP_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;
    }

    // Если в dwFlags не был выставлен флаг
    // CPTIMESTAMP_SIGNING_CERT_CHAIN_POLICY_IGNORE_NOT_CRITICAL_EKU_FLAG,
    // тогда возможно получение такого кода ошибки
    if (extraStatus.dwError & 
        CPCERT_TRUST_IS_NOT_CRITICAL_EKU)
    {
        DoSmthIfError(L"AppPolicy или EKU - не критическое расширение");
        if (SUCCEEDED(hr))
            hr = CERT_E_WRONG_USAGE;
    }

    // Если в dwFlags не был выставлен флаг
    // CPTIMESTAMP_SIGNING_CERT_CHAIN_POLICY_IGNORE_NOT_ONE_EKU_FLAG,
    // тогда возможно получение такого кода ошибки
    if (extraStatus.dwError & 
        CPCERT_TRUST_IS_NOT_ONE_EKU)
    {
        DoSmthIfError(L"В EKU указано больше одного использования");
        if (SUCCEEDED(hr))
            hr = CERT_E_WRONG_USAGE;
    }

    // Сертификат признан недопустимым для подписи штампов времени
    if (extraStatus.dwError & 
        CPCERT_TRUST_IS_NOT_VALID_FOR_USAGE)
    {
        DoSmthIfError(L"Сертификат был признан недопустимым для использования");
        if (SUCCEEDED(hr))
            hr = CERT_E_WRONG_USAGE;
    }        

    return hr;