SSL в WinHTTP
Службы HTTP Microsoft Windows (WinHTTP) поддерживают транзакции уровня SSL, включая сертификаты клиента. В этом разделе описываются понятия, связанные с транзакцией SSL и их обработкой с помощью WinHTTP.
Уровень безопасных сокетов
SSL — это установленный стандарт для обеспечения безопасности HTTP-транзакций. SSL предоставляет механизм для выполнения до 128-разрядного шифрования всех транзакций между клиентом и сервером. Он позволяет клиенту убедиться, что сервер принадлежит доверенной сущности с помощью сертификатов сервера. Он также позволяет серверу подтвердить удостоверение клиента с помощью сертификатов клиента.
Каждое из этих проблем, связанных с шифрованием, удостоверением сервера и удостоверением клиента, согласовываются в подтверждении SSL, возникающем при первом запросе клиента к ресурсу с сервера протокола HTTPS. По сути, каждый клиент и сервер представляют список обязательных и предпочтительных параметров. Если общий набор требований можно согласовать и выполнить, устанавливается SSL-подключение.
WinHTTP предоставляет интерфейс высокого уровня для использования SSL. Хотя сведения о подтверждении SSL и транзакции обрабатываются внутренне, WinHTTP позволяет получить уровни шифрования, указать протокол безопасности и взаимодействовать с сертификатами сервера и клиента. В следующих разделах содержатся сведения о создании приложений на основе WinHTTP, которые выбирают версию протокола SSL, проверяют сертификаты сервера и выбирают сертификаты клиента для отправки на HTTPS-серверы.
Сертификаты сервера
Сертификаты сервера отправляются с сервера на клиент, чтобы клиент смог получить открытый ключ для сервера и убедиться, что сервер проверен центром сертификации. Сертификаты могут содержать различные типы данных. Например, сертификат X.509 включает формат сертификата, серийный номер сертификата, алгоритм, используемый для подписи сертификата, имя центра сертификации ( ЦС), выдавшего сертификат, имя и открытый ключ сущности, запрашивающей сертификат, и подпись ЦС.
При использовании интерфейса программирования приложений WinHTTP (API) можно получить сертификат сервера, вызвав WinHttpQueryOption и указав флаг WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT. Сертификат сервера возвращается в WINHTTP_CERTIFICATE_INFO структуре. Если вы предпочитаете получить контекст сертификата, укажите вместо него флаг WINHTTP_OPTION_SERVER_CERT_CONTEXT.
Если сертификат сервера содержит ошибки, сведения об ошибке можно получить в функции обратного вызова состояния. Уведомление WINHTTP_CALLBACK_STATUS_SECURE_FAILURE указывает на ошибку с сертификатом сервера. Параметр lpvStatusInformation содержит один или несколько подробных флагов ошибок. Дополнительные сведения см. в WINHTTP_STATUS_CALLBACK.
Сертификаты клиента
Во время подтверждения SSL сервер может потребовать проверки подлинности. Клиент проходит проверку подлинности, указав действительный сертификат клиента серверу. WinHTTP позволяет выбрать и отправить сертификат из локального хранилища сертификатов . В следующих разделах описывается процесс, предоставляющий сертификаты клиента при использовании API WinHTTP или объекта WinHttpRequest.
WinHTTP API
Оба WinHttpSendRequest и WinHttpReceiveResponse могут не указывать, что запрос был неудачным, так как для сервера HTTPS требуется проверка подлинности. В этих случаях вызовите GetLastError для возврата ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. Получив эту ошибку, используйте соответствующие функции CryptoAPI для поиска соответствующего сертификата. Укажите, что этот сертификат следует отправить с помощью следующего запроса, вызвав WinHttpSetOption с флагом WINHTTP_OPTION_CLIENT_CERT_CONTEXT.
В следующем примере кода показано, как открыть хранилище сертификатов и найти сертификат на основе имени субъекта после возврата ошибки ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED.
if( !WinHttpReceiveResponse( hRequest, NULL ) )
{
if( GetLastError( ) == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED )
{
//MY is the store the certificate is in.
hMyStore = CertOpenSystemStore( 0, TEXT("MY") );
if( hMyStore )
{
pCertContext = CertFindCertificateInStore( hMyStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR,
(LPVOID) szCertName, //Subject string in the certificate.
NULL );
if( pCertContext )
{
WinHttpSetOption( hRequest,
WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
(LPVOID) pCertContext,
sizeof(CERT_CONTEXT) );
CertFreeCertificateContext( pCertContext );
}
CertCloseStore( hMyStore, 0 );
// NOTE: Application should now resend the request.
}
}
}
Перед повторной отправкой запроса, содержащего сертификат клиента, можно определить, является ли поддерживаемый уровень шифрования приемлемым для вашего приложения. Вызовите WinHttpQueryOption и укажите флаг WINHTTP_OPTION_SECURITY_FLAGS, чтобы определить уровень используемого шифрования.
Получение списка издателей для проверки подлинности SSL-клиента
Когда клиентское приложение WinHttp отправляет запрос на защищенный HTTP-сервер, требующий проверки подлинности SSL-клиента, WinHttp возвращает ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED если приложение не предоставило сертификат клиента. Для компьютеров под управлением Windows Server 2008 и Windows Vista WinHttp позволяет приложению получить список издателей сертификатов, предоставленный сервером в вызове проверки подлинности. Список издателей указывает список центров сертификации, авторизованных сервером для выдачи сертификатов клиента. Приложение фильтрует список издателей, чтобы получить необходимый сертификат.
Клиентское приложение WinHttp получает список издателей, когда WinHttpSendRequestили WinHttpReceiveResponse возвращает ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. При возврате этой ошибки приложение вызывает WinHttpQueryOption с параметром WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST. Параметр lpBuffer должен быть достаточно большим, чтобы содержать указатель на структуру SecPkgContext_IssuerListInfoEx. В следующем примере кода показано, как получить список издателей.
#include <windows.h>
#include <winhttp.h>
#include <schannel.h>
//...
void GetIssuerList(HINTERNET hRequest)
{
SecPkgContext_IssuerListInfoEx* pIssuerList = NULL;
DWORD dwBufferSize = sizeof(SecPkgContext_IssuerListInfoEx*);
if (WinHttpQueryOption(hRequest,
WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST,
&pIssuerList,
&dwBufferSize) == TRUE)
{
// Use the pIssuerList for cert store filtering.
GlobalFree(pIssuerList); // Free the issuer list when done.
}
}
Сведения в структуре SecPkgContext_IssuerListInfoExcIssuers и aIssuers можно использовать для поиска сертификата, как показано в приведенном ниже примере кода. Дополнительные сведения см. в CertFindChainStore.
PCERT_CONTEXT pClientCert = NULL;
PCCERT_CHAIN_CONTEXT pClientCertChain = NULL;
CERT_CHAIN_FIND_BY_ISSUER_PARA SrchCriteria;
::ZeroMemory(&SrchCriteria, sizeof(CERT_CHAIN_FIND_BY_ISSUER_PARA));
SrchCriteria.cbSize = sizeof(CERT_CHAIN_FIND_BY_ISSUER_PARA);
SrchCriteria.cIssuer = pIssuerList->cIssuers;
SrchCriteria.rgIssuer = pIssuerList->aIssuers;
pClientCertChain = CertFindChainInStore(
hClientCertStore,
X509_ASN_ENCODING,
CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG |
// Do not perform wire download when building chains.
CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG,
// Do not search pCacheEntry->_ClientCertStore
// for issuer certs.
CERT_CHAIN_FIND_BY_ISSUER,
&SrchCriteria,
NULL);
if (pClientCertChain)
{
pClientCert = (PCERT_CONTEXT) pClientCertChain->rgpChain[0]->rgpElement[0]->pCertContext;
CertDuplicateCertificateContext(pClientCert);
CertFreeCertificateChain(pClientCertChain);
pClientCertChain = NULL;
}
Необязательные SSL-сертификаты клиента
Начиная с Windows Server 2008 и Windows Vista API WinHttp поддерживает необязательные сертификаты клиента. Когда сервер запрашивает сертификат клиента, WinHttpSendRequestили WinHttpRecieveResponse возвращает ошибку ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. Если сервер запрашивает сертификат, но не требует его, приложение может указать этот параметр, чтобы указать, что у него нет сертификата. Сервер может выбрать другую схему проверки подлинности или разрешить анонимный доступ к серверу. Приложение задает макрос WINHTTP_NO_CLIENT_CERT_CONTEXT в параметре lpBufferWinHttpSetOption, как показано в следующем примере кода.
BOOL fRet = WinHttpSetOption ( hRequest,
WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
WINHTTP_NO_CLIENT_CERT_CONTEXT,
0);
Если WINHTTP_NO_CLIENT_CERT_CONTEXT задан, и серверу по-прежнему требуется сертификат клиента, он может отправить код состояния HTTP 403. Дополнительные сведения см. в параметре WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST.
Объект WinHttpRequest
Используйте методSetClientCertificate объекта WinHttpRequest, чтобы выбрать сертификаты клиента для отправки на сервер с запросом. Выберите сертификат, указав строку выбора сертификата с помощью метода SetClientCertificate. Строка выбора сертификата состоит из расположения сертификата, хранилища сертификатови имени субъекта, разделенного обратными косыми чертами. В следующей таблице перечислены компоненты для этой строки выбора.
Компонент | Описание | Возможные значения |
---|---|---|
Местоположение | Определяет раздел реестра, в котором хранятся сертификаты. | Возможные значения — "LOCAL_MACHINE", чтобы указать, чтохранилища сертификатовнаходится в HKEY_LOCAL_MACHINE и "CURRENT_USER", чтобы указать, что хранилища сертификатов находится под неименованнымHKEY_CURRENT_USER. Этот компонент учитывает регистр. |
хранилища сертификатов | Указывает имя хранилища сертификатов , содержащего соответствующий сертификат. | Типичные хранилища сертификатов: MY, Root и TrustedPeople. Этот компонент учитывает регистр. |
Имя субъекта | Определяет сертификат в указанном хранилище сертификатов . Выбран первый сертификат, содержащий строку, указанную для этого компонента. | Имя субъекта может быть любой строкой. Пустая строка указывает, что следует использовать первый сертификат в хранилище сертификатов. Этот компонент не учитывает регистр. |
Хранилище сертификатов имя и расположение являются необязательными компонентами. Однако если указать хранилища сертификатов, необходимо также указать расположение хранилища сертификатов. Расположение по умолчанию — CURRENT_USER, а хранилища сертификатов по умолчанию — MY.
В следующем примере кода показано, как указать, что сертификат с субъектом "Мой сертификат Middle-Tier" должен быть выбран из хранилища сертификатов "Персональный" в реестре в HKEY_LOCAL_MACHINE.
HttpReq.SetClientCertificate("LOCAL_MACHINE\Personal\My Middle-Tier Certificate")
Заметка
На некоторых языках обратная косая черта является escape-символом. Не забудьте изменить строку выбора сертификата в учетную запись. Например, в Microsoft JScript используйте две смежные обратные косые очки вместо одного.
Если сертификат не указан, а для HTTPS-сервера требуется сертификат клиента, WinHTTP выбирает первый сертификат в хранилище сертификатов по умолчанию . Если сертификаты отсутствуют, возникает ошибка. Если сертификат не принят, сервер возвращает код состояния 403, чтобы указать, что запрос не может быть выполнен. Затем можно выбрать более подходящий сертификат с SetClientCertificate и повторно отправить запрос.