Пример проверки цепочки сертификатов на корректность использования закрытого ключа сертификата для подписи выпущенного сертификата, базового или дельта CRL, содержащегося в цепочке, подписи CTL с использованием флага CPCERT_CHAIN_POLICY_PRIVATEKEY_USAGE_PERIOD в функции 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, выпущенный конечный сертификат или
// промежуточного ЦС, если это сертификат ЦС.
//
// Выставляем необходимые флаги
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,
¶,
&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;