SSL in WinHTTP

Microsoft Windows HTTP Services (WinHTTP) unterstützt SSL-Transaktionen (Secure Sockets Layer), einschließlich Clientzertifikaten. In diesem Thema werden die Konzepte einer SSL-Transaktion und deren Behandlung mit WinHTTP erläutert.

Secure Sockets Layer

SSL ist ein etablierter Standard zum Sicherstellen sicherer HTTP-Transaktionen. SSL bietet einen Mechanismus zum Ausführen einer 128-Bit-Verschlüsselung für alle Transaktionen zwischen Client und Server. Der Client kann mithilfe von Serverzertifikaten überprüfen, ob der Server zu einer vertrauenswürdigen Entität gehört. Außerdem kann der Server die Identität des Clients mit Clientzertifikaten bestätigen.

Jedes dieser Probleme verschlüsselung, Serveridentität und Clientidentität werden 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 dar. Wenn ein allgemeiner Satz von Anforderungen vereinbart und erfüllt werden kann, wird eine SSL-Verbindung hergestellt.

WinHTTP bietet eine high level-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 von WinHTTP-basierten Anwendungen, die eine SSL-Protokollversion auswählen, Untersuchen von Serverzertifikaten und Auswählen von Clientzertifikaten, 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 z. B. das Format des Zertifikats, die Seriennummer des Zertifikats, den Algorithmus zum Signieren des Zertifikats, den Namen der Zertifizierungsstelle, die das Zertifikat ausgestellt hat, den Namen und den ö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 WINHTTP _ OPTION SECURITY _ CERTIFICATE _ _ STRUCT-Flag angeben. Das Serverzertifikat wird in einer WINHTTP _ CERTIFICATE _ INFO-Struktur zurückgegeben. Wenn Sie den Zertifikatkontext abrufen möchten, geben Sie stattdessen das WINHTTP _ OPTION SERVER _ _ CERT _ CONTEXT-Flag an.

Wenn ein Serverzertifikat Fehler enthält, können Details zu diesem Fehler in der Statusrückruffunktion 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 dem Server ein gültiges Clientzertifikat zur Verfügung gestellt wird. Mit WinHTTP können Sie ein Zertifikat aus einem lokalen Zertifikatspeicherauswählen und senden. In den folgenden Abschnitten wird der Prozess beschrieben, der Clientzertifikate bereitstellt, wenn entweder die WinHTTP-API oder das WinHttpRequest-Objekt verwendet wird.

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 _ NEEDEDzurückgibt. Verwenden Sie nach dem Empfang dieses Fehlers die entsprechenden CryptoAPI-Funktionen, um nach einem geeigneten Zertifikat zu suchen. Geben Sie an, dass dieses Zertifikat mit der nächsten Anforderung gesendet werden soll, indem Sie WinHttpSetOption mit dem WINHTTP _ OPTION CLIENT _ _ CERT _ CONTEXT-Flag aufrufen.

Das folgende Codebeispiel zeigt, wie Sie einen Zertifikatspeicher öffnen und ein Zertifikat basierend auf dem Antragstellernamen suchen, nachdem der _ FEHLER WINHTTP _ CLIENT _ _ AUTH CERT _ NEEDED 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.
      }
    }
  }

Vor dem erneuten Senden einer Anforderung, die ein Clientzertifikat enthält, können Sie ermitteln, ob die unterstützte Verschlüsselungsstufe 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.

Abrufen der Ausstellerliste für die SSL-Clientauthentifizierung

Wenn die WinHttp-Clientanwendung eine Anforderung an einen sicheren HTTP-Server sendet, der eine SSL-Clientauthentifizierung erfordert, gibt WinHttp ein ERROR _ WINHTTP _ CLIENT _ _ AUTH CERT _ NEEDED zurück, wenn die Anwendung kein Clientzertifikat bereitgestellt hat. Bei Computern, die auf Windows Server 2008 und Windows Vista ausgeführt werden, ermöglicht WinHttp der Anwendung, die vom Server in der Authentifizierungsaufforderung angegebene Zertifikatausstellerliste abzurufen. Die Ausstellerliste gibt eine Liste von Zertifizierungsstellen (Certificate Authorities, CAs) an, die vom Server zum Ausstellen von Clientzertifikaten autorisiert sind. Die Anwendung filtert die Ausstellerliste, um das erforderliche Zertifikat abzurufen.

Die WinHttp-Clientanwendung ruft die Ausstellerliste ab, wenn WinHttpSendRequestoder WinHttpReceiveResponse ERROR _ 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. Das folgende Codebeispiel zeigt, 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 SSL-Clientzertifikate

Ab Windows Server 2008 und Windows Vista unterstützt die WinHttp-API optionale Clientzertifikate. Wenn der Server ein Clientzertifikat anfordert, gibt WinHttpSendRequestoder WinHttpRecieveResponse einen FEHLER _ 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 es kein Zertifikat besitzt. 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 WINHTTP _ NO _ CLIENT _ CERT _ CONTEXT festgelegt ist und der Server weiterhin ein Clientzertifikat benötigt, wird möglicherweise ein HTTP-Statuscode 403 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 Zertifikatspeicherund 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 _ befindet.
und "CURRENT _ USER", um anzugeben, dass sich der Zertifikatspeicher unter dem nicht angenommenen HKEY _ 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".

Das folgende Codebeispiel zeigt, wie Sie angeben, dass ein Zertifikat mit dem Betreff "My Middle-Tier Certificate" aus dem Zertifikatspeicher "Persönlich" 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 beispielsweise in Microsoft JScript zwei angrenzende umgekehrte Schrägstriche anstelle von einem.

Wenn Sie kein Zertifikat angeben und ein HTTPS-Server ein Clientzertifikat erfordert, wählt WinHTTP das erste Zertifikat im Standardzertifikatspeicheraus. Wenn keine Zertifikate vorhanden sind, wird ein Fehler ausgelöst. Wenn das Zertifikat nicht akzeptiert wird, gibt der Server den Statuscode 403 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.