SSL in WinHTTP
Microsoft Windows HTTP Services (WinHTTP) unterstützt SSL-Transaktionen (Secure Sockets Layer), einschließlich Clientzertifikaten. In diesem Thema werden konzepte erläutert, die an einer SSL-Transaktion beteiligt sind und wie sie mithilfe von WinHTTP behandelt werden.
Secure Sockets Layer
SSL ist ein etablierter Standard für sichere HTTP-Transaktionen. SSL bietet einen Mechanismus zum Ausführen von bis zu 128-Bit-Verschlüsselung für alle Transaktionen zwischen Client und Server. Dadurch kann der Client überprüfen, ob der Server zu einer vertrauenswürdigen Entität gehört, indem Serverzertifikate verwendet werden. Außerdem kann der Server die Identität des Clients mit Clientzertifikaten bestätigen.
Jedes dieser Probleme mit Verschlüsselung, Serveridentität und Clientidentität wird im SSL-Handshake ausgehandelt, der auftritt, wenn ein Client zum ersten Mal eine Ressource von einem HTTPS-Server (Secure Hypertext Transfer Protocol) anfordert. Im Wesentlichen stellen Client und Server jeweils eine Liste der erforderlichen und bevorzugten Einstellungen vor. Wenn ein gemeinsamer Satz von Anforderungen vereinbart und erfüllt werden kann, wird eine SSL-Verbindung hergestellt.
WinHTTP bietet eine allgemeine Schnittstelle für die Verwendung von SSL. Während die Details des SSL-Handshakes und der Transaktion intern verarbeitet werden, können Sie mit WinHTTP Verschlüsselungsebenen abrufen, das Sicherheitsprotokoll angeben und mit Server- und Clientzertifikaten interagieren. Die folgenden Abschnitte enthalten Details zum Erstellen winHTTP-basierter Anwendungen, die eine SSL-Protokollversion auswählen, Serverzertifikate untersuchen und Clientzertifikate auswählen, die an HTTPS-Server gesendet werden sollen.
Serverzertifikate
Serverzertifikate werden vom Server an den Client gesendet, damit der Client einen öffentlichen Schlüssel für den Server abrufen und sicherstellen kann, dass der Server von einer Zertifizierungsstelle überprüft wurde. Zertifikate können verschiedene Datentypen enthalten. Ein X.509-Zertifikat enthält beispielsweise das Format des Zertifikats, die Seriennummer des Zertifikats, den Algorithmus, der zum Signieren des Zertifikats verwendet wird, den Namen der Zertifizierungsstelle (CA), die das Zertifikat ausgestellt hat, den Namen und öffentlichen Schlüssel der Entität, die das Zertifikat anfordert, und die Signatur der Zertifizierungsstelle.
Wenn Sie die WinHTTP-API (Application Programming Interface) verwenden, können Sie ein Serverzertifikat abrufen, indem Sie WinHttpQueryOption aufrufen und das flag WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT angeben. Das Serverzertifikat wird in einer WINHTTP_CERTIFICATE_INFO-Struktur zurückgegeben. Wenn Sie den Zertifikatkontext lieber abrufen möchten, geben Sie stattdessen das flag WINHTTP_OPTION_SERVER_CERT_CONTEXT an.
Wenn ein Serverzertifikat Fehler enthält, können Details zum Fehler in der rückruffunktion status abgerufen werden. Die WINHTTP_CALLBACK_STATUS_SECURE_FAILURE Benachrichtigung weist auf einen Fehler mit einem Serverzertifikat hin. Der lpvStatusInformation-Parameter enthält mindestens ein detailliertes Fehlerflag. Weitere Informationen finden Sie unter WINHTTP_STATUS_CALLBACK .
Clientzertifikate
Während des SSL-Handshakes erfordert der Server möglicherweise eine Authentifizierung. Der Client wird authentifiziert, indem ein gültiges Clientzertifikat für den Server bereitgestellt wird. Mit WinHTTP können Sie ein Zertifikat aus einem lokalen Zertifikatspeicher auswählen und senden. In den folgenden Abschnitten wird der Prozess beschrieben, der Clientzertifikate bei Verwendung der WinHTTP-API oder des WinHttpRequest-Objekts bereitstellt.
WinHTTP-API
Sowohl WinHttpSendRequest als auch WinHttpReceiveResponse können nicht angeben, dass eine Anforderung nicht erfolgreich war, da der HTTPS-Server eine Authentifizierung erfordert. Rufen Sie in diesen Fällen GetLastError auf, um ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED zurückgibt. Wenn Sie diesen Fehler erhalten, verwenden Sie die entsprechenden CryptoAPI-Funktionen , um ein geeignetes Zertifikat zu finden. Geben Sie an, dass dieses Zertifikat mit der nächsten Anforderung gesendet werden soll, indem Sie WinHttpSetOption mit dem flag WINHTTP_OPTION_CLIENT_CERT_CONTEXT aufrufen.
Das folgende Codebeispiel zeigt, wie Sie einen Zertifikatspeicher öffnen und ein Zertifikat basierend auf dem Antragstellernamen suchen, nachdem der ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED Fehler zurückgegeben wurde.
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.
}
}
}
Bevor Sie eine Anforderung erneut senden, die ein Clientzertifikat enthält, können Sie ermitteln, ob die unterstützte Verschlüsselungsebene für Ihre Anwendung akzeptabel ist. Rufen Sie WinHttpQueryOption auf, und geben Sie das flag WINHTTP_OPTION_SECURITY_FLAGS an, um die verwendete Verschlüsselungsebene zu bestimmen.
Ausstellerlistenabruf für SSL-Clientauthentifizierung
Wenn die WinHttp-Clientanwendung eine Anforderung an einen sicheren HTTP-Server sendet, der eine SSL-Clientauthentifizierung erfordert, gibt WinHttp eine ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED zurück, wenn die Anwendung kein Clientzertifikat bereitgestellt hat. Bei Computern unter Windows Server 2008 und Windows Vista ermöglicht WinHttp der Anwendung das Abrufen der Zertifikatausstellerliste, die vom Server in der Authentifizierungsanforderung bereitgestellt wird. Die Ausstellerliste gibt eine Liste der Zertifizierungsstellen (Certificate Authorities, CAs) an, die vom Server zum Ausstellen von Clientzertifikaten autorisiert sind. Die Anwendung filtert die Ausstellerliste, um das erforderliche Zertifikat zu erhalten.
Die WinHttp-Clientanwendung ruft die Ausstellerliste ab, wenn WinHttpSendRequest oder WinHttpReceiveResponseERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED zurückgibt. Wenn dieser Fehler zurückgegeben wird, ruft die Anwendung WinHttpQueryOption mit der Option WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST auf. Der lpBuffer-Parameter muss groß genug sein, um einen Zeiger auf die SecPkgContext_IssuerListInfoEx-Struktur zu enthalten. Im folgenden Codebeispiel wird gezeigt, wie die Ausstellerliste abgerufen wird.
#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.
}
}
Die Informationen in der SecPkgContext_IssuerListInfoEx-Struktur, cIssuers und aIssuers, können verwendet werden, um nach dem Zertifikat zu suchen, wie im folgenden Codebeispiel gezeigt. Weitere Informationen finden Sie unter CertFindChainInStore.
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;
}
Optionale CLIENT-SSL-Zertifikate
Ab Windows Server 2008 und Windows Vista unterstützt die WinHttp-API optionale Clientzertifikate. Wenn der Server ein Clientzertifikat anfordert, gibt WinHttpSendRequest oder WinHttpRecieveResponse einen ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED Fehler zurück. Wenn der Server das Zertifikat anfordert, es aber nicht benötigt, kann die Anwendung diese Option angeben, um anzugeben, dass kein Zertifikat vorhanden ist. Der Server kann ein anderes Authentifizierungsschema auswählen oder anonymen Zugriff auf den Server zulassen. Die Anwendung gibt das WINHTTP_NO_CLIENT_CERT_CONTEXT Makro im lpBuffer-Parameter von WinHttpSetOption an, wie im folgenden Codebeispiel gezeigt.
BOOL fRet = WinHttpSetOption ( hRequest,
WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
WINHTTP_NO_CLIENT_CERT_CONTEXT,
0);
Wenn die WINHTTP_NO_CLIENT_CERT_CONTEXT festgelegt ist und der Server weiterhin ein Clientzertifikat benötigt, wird möglicherweise ein 403-HTTP-status-Code gesendet. Weitere Informationen finden Sie unter der Option WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST .
WinHttpRequest-Objekt
Verwenden Sie die SetClientCertificate-Methode des WinHttpRequest-Objekts , um Clientzertifikate auszuwählen, die mit einer Anforderung an den Server gesendet werden sollen. Wählen Sie ein Zertifikat aus, indem Sie eine Zertifikatauswahlzeichenfolge mit der SetClientCertificate-Methode angeben. Die Zertifikatauswahlzeichenfolge besteht aus dem Zertifikatspeicherort, dem Zertifikatspeicher und dem Antragstellernamen, die durch umgekehrte Schrägstriche getrennt sind. In der folgenden Tabelle sind Die Komponenten für diese Auswahlzeichenfolge aufgeführt.
Komponente | BESCHREIBUNG | Mögliche Werte |
---|---|---|
Standort | Bestimmt den Registrierungsschlüssel, unter dem die Zertifikate gespeichert werden. | Die möglichen Werte sind "LOCAL_MACHINE", um anzugeben, dass sich der Zertifikatspeicher unter HKEY_LOCAL_MACHINE und "CURRENT_USER", um anzugeben, dass sich der Zertifikatspeicher unter der nicht identitätswechseltenHKEY_CURRENT_USER befindet. Bei dieser Komponente wird die Groß-/Kleinschreibung beachtet. |
Zertifikatspeicher | Gibt den Namen des Zertifikatspeichers an, der das relevante Zertifikat enthält. | Typische Zertifikatspeicher sind "MY", "Root" und "TrustedPeople". Bei dieser Komponente wird die Groß-/Kleinschreibung beachtet. |
Antragstellername | Identifiziert ein Zertifikat innerhalb des angegebenen Zertifikatspeichers. Das erste Zertifikat, das die für diese Komponente angegebene Zeichenfolge enthält, ist ausgewählt. | Der Antragstellername kann eine beliebige Zeichenfolge sein. Eine leere Zeichenfolge gibt an, dass das erste Zertifikat im Zertifikatspeicher verwendet werden soll. Bei dieser Komponente wird die Groß-/Kleinschreibung nicht beachtet. |
Name und Speicherort des Zertifikatspeichers sind optionale Komponenten. Wenn Sie jedoch einen Zertifikatspeicher angeben, müssen Sie auch den Speicherort dieses Zertifikatspeichers angeben. Der Standardspeicherort ist CURRENT_USER, und der Standardzertifikatspeicher ist "MY".
Im folgenden Codebeispiel wird gezeigt, wie sie angeben, dass ein Zertifikat mit dem Betreff "My Middle-Tier Certificate" aus dem Zertifikatspeicher "Personal" in der Registrierung unter HKEY_LOCAL_MACHINE ausgewählt werden soll.
HttpReq.SetClientCertificate("LOCAL_MACHINE\Personal\My Middle-Tier Certificate")
Hinweis
In einigen Sprachen ist der umgekehrte Schrägstrich ein Escapezeichen. Denken Sie daran, die Zertifikatauswahlzeichenfolge zu ändern, um dies zu berücksichtigen. Verwenden Sie in Microsoft JScript beispielsweise zwei benachbarte umgekehrte Schrägstriche anstelle eines.
Wenn Sie kein Zertifikat angeben und ein HTTPS-Server ein Clientzertifikat erfordert, wählt WinHTTP das erste Zertifikat im Standardzertifikatspeicher aus. Wenn keine Zertifikate vorhanden sind, wird ein Fehler ausgelöst. Wenn das Zertifikat nicht akzeptiert wird, gibt der Server den Code 403 status zurück, um anzugeben, dass die Anforderung nicht erfüllt werden kann. Sie können dann mit SetClientCertificate ein geeigneteres Zertifikat auswählen und die Anforderung erneut senden.