Delen via


SSL in WinHTTP

Microsoft Windows HTTP Services (WinHTTP) ondersteunt SSL-transacties (Secure Sockets Layer), inclusief clientcertificaten. In dit onderwerp worden concepten uitgelegd die betrokken zijn bij een SSL-transactie en hoe ze worden verwerkt met Behulp van WinHTTP.

Secure Sockets Layer

SSL is een gevestigde standaard voor het garanderen van beveiligde HTTP-transacties. SSL biedt een mechanisme voor het uitvoeren van maximaal 128-bits versleuteling voor alle transacties tussen de client en de server. Hiermee kan de client controleren of de server deel uitmaakt van een vertrouwde entiteit via het gebruik van servercertificaten. Hiermee kan de server ook de identiteit van de client met clientcertificaten bevestigen.

Elk van deze problemen versleuteling, serveridentiteit en clientidentiteit worden onderhandeld in de SSL-handshake die optreedt wanneer een client voor het eerst een resource aanvraagt vanaf een HTTPS-server (Secure Hypertext Transfer Protocol). In wezen presenteren de client en server elk een lijst met vereiste en voorkeursinstellingen. Als aan een gemeenschappelijke set vereisten kan worden voldaan, wordt een SSL-verbinding tot stand gebracht.

WinHTTP biedt een interface op hoog niveau voor het gebruik van SSL. Hoewel de details van de SSL-handshake en -transactie intern worden verwerkt, kunt u met WinHTTP versleutelingsniveaus ophalen, het beveiligingsprotocol opgeven en communiceren met server- en clientcertificaten. In de volgende secties vindt u meer informatie over het maken van WinHTTP-toepassingen die een SSL-protocolversie selecteren, servercertificaten onderzoeken en clientcertificaten selecteren die u naar HTTPS-servers wilt verzenden.

Servercertificaten

Servercertificaten worden van de server naar de client verzonden, zodat de client een openbare sleutel voor de server kan verkrijgen en ervoor kan zorgen dat de server is geverifieerd door een certificeringsinstantie. Certificaten kunnen verschillende typen gegevens bevatten. Een X.509-certificaat bevat bijvoorbeeld de indeling van het certificaat, het serienummer van het certificaat, het algoritme dat wordt gebruikt om het certificaat te ondertekenen, de naam van de certificeringsinstantie (CA) die het certificaat heeft uitgegeven, de naam en openbare sleutel van de entiteit die het certificaat aanvraagt en de handtekening van de CA.

Wanneer u de WinHTTP Application Programming Interface (API) gebruikt, kunt u een servercertificaat ophalen door WinHttpQueryOption- aan te roepen en de vlag WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT op te geven. Het servercertificaat wordt geretourneerd in een WINHTTP_CERTIFICATE_INFO structuur. Als u liever de certificaatcontext ophaalt, geeft u in plaats daarvan de WINHTTP_OPTION_SERVER_CERT_CONTEXT vlag op.

Als een servercertificaat fouten bevat, kunnen details over de fout worden verkregen in de functie status callback. De melding WINHTTP_CALLBACK_STATUS_SECURE_FAILURE geeft een fout met een servercertificaat aan. De parameter lpvStatusInformation bevat een of meer gedetailleerde foutvlagken. Zie WINHTTP_STATUS_CALLBACK voor meer informatie.

Clientcertificaten

Tijdens de SSL-handshake is mogelijk verificatie vereist voor de server. De client wordt geverifieerd door een geldig clientcertificaat op te geven aan de server. Met WinHTTP kunt u een certificaat selecteren en verzenden vanuit een lokaal certificaatarchief. In de volgende secties wordt het proces beschreven dat clientcertificaten levert bij het gebruik van de WinHTTP-API of het WinHttpRequest-object.

WinHTTP-API

Zowel WinHttpSendRequest als WinHttpReceiveResponse- kan niet aangeven dat een aanvraag mislukt is omdat de HTTPS-server verificatie vereist. In deze gevallen roept u GetLastError- aan om ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED te retourneren. Wanneer u deze fout ontvangt, gebruikt u de juiste CryptoAPI--functies om een geschikt certificaat te vinden. Geef aan dat dit certificaat moet worden verzonden met de volgende aanvraag door WinHttpSetOption- aan te roepen met de vlag WINHTTP_OPTION_CLIENT_CERT_CONTEXT.

In het volgende codevoorbeeld ziet u hoe u een certificaatarchief opent en een certificaat zoekt op basis van de onderwerpnaam nadat de fout ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED is geretourneerd.

  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.
      }
    }
  }

Voordat u een aanvraag met een clientcertificaat opnieuw verzendt, kunt u bepalen of het ondersteunde versleutelingsniveau acceptabel is voor uw toepassing. Roep WinHttpQueryOption- aan en geef de vlag WINHTTP_OPTION_SECURITY_FLAGS op om het gebruikte versleutelingsniveau te bepalen.

Lijst met verleners ophalen voor SSL-clientverificatie

Wanneer de WinHttp-clienttoepassing een aanvraag verzendt naar een beveiligde HTTP-server waarvoor SSL-clientverificatie is vereist, retourneert WinHttp een ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED als de toepassing geen clientcertificaat heeft opgegeven. Voor computers met Windows Server 2008 en Windows Vista kan WinHttp de lijst met certificaatverleners ophalen die door de server is opgegeven in de verificatievraag. De lijst met verleners bevat een lijst met certificeringsinstanties (CA's) die door de server zijn geautoriseerd om clientcertificaten uit te geven. De toepassing filtert de lijst met verleners om het vereiste certificaat te verkrijgen.

De WinHttp-clienttoepassing haalt de lijst met verleners op wanneer WinHttpSendRequestof WinHttpReceiveResponse retourneert ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. Wanneer deze fout wordt geretourneerd, roept de toepassing WinHttpQueryOption- aan met de optie WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST. De parameter lpBuffer moet groot genoeg zijn om een aanwijzer naar de SecPkgContext_IssuerListInfoEx structuur te bevatten. In het volgende codevoorbeeld ziet u hoe u de lijst met verleners ophaalt.

#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.
  }
}

De informatie in de SecPkgContext_IssuerListInfoEx structuur, cIssuers en aIssuers, kan worden gebruikt om naar het certificaat te zoeken, zoals wordt weergegeven in het onderstaande codevoorbeeld. Zie CertFindChainInStorevoor meer informatie.

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;
}

Optionele SSL-certificaten voor clients

Vanaf Windows Server 2008 en Windows Vista ondersteunt de WinHttp-API optionele clientcertificaten. Wanneer de server een clientcertificaat aanvraagt, retourneert WinHttpSendRequestof WinHttpRecieveResponse- een ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED-fout. Als de server het certificaat aanvraagt, maar dit niet vereist, kan de toepassing deze optie opgeven om aan te geven dat het geen certificaat heeft. De server kan een ander verificatieschema kiezen of anonieme toegang tot de server toestaan. De toepassing geeft de WINHTTP_NO_CLIENT_CERT_CONTEXT macro op in de lpBuffer parameter van WinHttpSetOption-, zoals wordt weergegeven in het volgende codevoorbeeld.

BOOL fRet = WinHttpSetOption ( hRequest,
                               WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
                               WINHTTP_NO_CLIENT_CERT_CONTEXT,
                               0);

Als de WINHTTP_NO_CLIENT_CERT_CONTEXT is ingesteld en de server nog steeds een clientcertificaat vereist, kan er een HTTP-statuscode van 403 worden verzonden. Zie de optie WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST voor meer informatie.

WinHttpRequest-object

Gebruik de methode SetClientCertificate van het WinHttpRequest-object om clientcertificaten te selecteren die met een aanvraag naar de server moeten worden verzonden. Selecteer een certificaat door een certificaatselectiereeks op te geven met de SetClientCertificate methode. De certificaatselectietekenreeks bestaat uit de certificaatlocatie, certificaatarchiefen de onderwerpnaam gescheiden door backslashes. De volgende tabel bevat onderdelen voor deze selectietekenreeks.

Bestanddeel Beschrijving Mogelijke waarden
Plaats Bepaalt de registersleutel waaronder de certificaten worden opgeslagen. De mogelijke waarden zijn 'LOCAL_MACHINE' om aan te geven dat het certificaatarchief onder HKEY_LOCAL_MACHINE
en CURRENT_USER om aan te geven dat het certificaatarchief onder de niet-geïmiteerdeHKEY_CURRENT_USER valt.
Dit onderdeel is hoofdlettergevoelig.
certificaatarchief Geeft de naam aan van het certificaatarchief dat het relevante certificaat bevat. Typische certificaatarchieven zijn 'MY', 'Root' en 'TrustedPeople'. Dit onderdeel is hoofdlettergevoelig.
Onderwerpnaam Identificeert een certificaat in het opgegeven certificaatarchief. Het eerste certificaat dat de tekenreeks bevat die voor dit onderdeel is opgegeven, wordt geselecteerd. De onderwerpnaam kan elke tekenreeks zijn. Een lege tekenreeks geeft aan dat het eerste certificaat in het certificaatarchief moet worden gebruikt. Dit onderdeel is niet hoofdlettergevoelig.

Het certificaatarchief naam en locatie zijn optionele onderdelen. Als u echter een certificaatarchief opgeeft, moet u ook de locatie van dat certificaatarchief opgeven. De standaardlocatie is CURRENT_USER en het standaardcertificaatarchief is 'MIJN'.

In het volgende codevoorbeeld ziet u hoe u kunt opgeven dat een certificaat met het onderwerp 'Mijn Middle-Tier-certificaat' moet worden gekozen uit het certificaatarchief in het register onder HKEY_LOCAL_MACHINE.

HttpReq.SetClientCertificate("LOCAL_MACHINE\Personal\My Middle-Tier Certificate")

Notitie

In sommige talen is de backslash een escape-teken. Vergeet niet om de certificaatselectietekenreeks te wijzigen om hiervoor rekening te houden. Gebruik in Microsoft JScript bijvoorbeeld twee aangrenzende backslashes in plaats van één.

Als u geen certificaat opgeeft en een HTTPS-server een clientcertificaat vereist, selecteert WinHTTP het eerste certificaat in het standaardcertificaatarchief certificaatarchief. Als er geen certificaten bestaan, wordt er een fout gegenereerd. Als het certificaat niet wordt geaccepteerd, retourneert de server een 403-statuscode om aan te geven dat de aanvraag niet kan worden uitgevoerd. Vervolgens kunt u een geschikter certificaat kiezen met SetClientCertificate en de aanvraag opnieuw verzenden.