Пример проверки цепочки и использования конечного сертификата для выполнения операций подписи 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, ¶, &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;