SSL dans WinHTTP
Les services HTTP Microsoft Windows (WinHTTP) prennent en charge les transactions SSL (Secure Sockets Layer), y compris les certificats clients. Cette rubrique explique les concepts impliqués dans une transaction SSL et comment ils sont gérés à l’aide de WinHTTP.
Couche Sockets sécurisés
SSL est une norme établie pour garantir des transactions HTTP sécurisées. SSL fournit un mécanisme permettant d’effectuer jusqu’à 128 bits de chiffrement sur toutes les transactions entre le client et le serveur. Il permet au client de vérifier que le serveur appartient à une entité approuvée via l’utilisation de certificats de serveur. Il permet également au serveur de confirmer l’identité du client avec des certificats clients.
Chacun de ces problèmes de chiffrement, d’identité de serveur et d’identité cliente est négocié dans la négociation SSL qui se produit lorsqu’un client demande d’abord une ressource à partir d’un serveur HTTPS (Secure Hypertext Transfer Protocol). Essentiellement, le client et le serveur présentent chacune une liste de paramètres obligatoires et préférés. Si un ensemble commun d’exigences peut être convenu et satisfait, une connexion SSL est établie.
WinHTTP fournit une interface de haut niveau pour l’utilisation de SSL. Bien que les détails de la négociation ET de la transaction SSL soient gérés en interne, WinHTTP vous permet de récupérer des niveaux de chiffrement, de spécifier le protocole de sécurité et d’interagir avec les certificats serveur et client. Les sections suivantes fournissent des détails sur la création d’applications WinHTTP qui choisissent une version du protocole SSL, examinent les certificats de serveur et sélectionnent les certificats clients à envoyer aux serveurs HTTPS.
Certificats de serveur
Les certificats de serveur sont envoyés du serveur au client afin que le client puisse obtenir une clé publique pour le serveur et s’assurer que le serveur a été vérifié par une autorité de certification. Les certificats peuvent contenir différents types de données. Par exemple, un certificat X.509 inclut le format du certificat, le numéro de série du certificat, l’algorithme utilisé pour signer le certificat, le nom de l’autorité de certification qui a émis le certificat, le nom et la clé publique de l’entité qui demande le certificat et la signature de l’autorité de certification.
Lorsque vous utilisez l’interface de programmation d’application WinHTTP (API), vous pouvez récupérer un certificat de serveur en appelant WinHttpQueryOption et en spécifiant l’indicateur de WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT. Le certificat de serveur est retourné dans une structure WINHTTP_CERTIFICATE_INFO. Si vous préférez récupérer le contexte de certificat, spécifiez plutôt l’indicateur WINHTTP_OPTION_SERVER_CERT_CONTEXT.
Si un certificat de serveur contient des erreurs, des détails sur l’erreur peuvent être obtenus dans la fonction de rappel d’état. La notification WINHTTP_CALLBACK_STATUS_SECURE_FAILURE indique une erreur avec un certificat de serveur. Le paramètre lpvStatusInformation contient un ou plusieurs indicateurs d’erreur détaillés. Pour plus d’informations, consultez WINHTTP_STATUS_CALLBACK.
Certificats clients
Pendant la négociation SSL, le serveur peut nécessiter l’authentification. Le client est authentifié en fournissant un certificat client valide au serveur. WinHTTP vous permet de sélectionner et d’envoyer un certificat à partir d’un magasin de certificats local. Les sections suivantes décrivent le processus qui fournit des certificats clients lors de l’utilisation de l’API WinHTTP ou de l’objet WinHttpRequest.
WinHTTP API
Les deux WinHttpSendRequest et WinHttpReceiveResponse peuvent ne pas indiquer qu’une demande a échoué, car le serveur HTTPS requiert l’authentification. Dans ce cas, appelez GetLastError pour renvoyer ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. Lors de la réception de cette erreur, utilisez les fonctions de CryptoAPI appropriées pour rechercher un certificat approprié. Indiquez que ce certificat doit être envoyé avec la requête suivante en appelant WinHttpSetOption avec l’indicateur de WINHTTP_OPTION_CLIENT_CERT_CONTEXT.
L’exemple de code suivant montre comment ouvrir un magasin de certificats et localiser un certificat en fonction du nom de l’objet après que l’erreur de ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED a été retournée.
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.
}
}
}
Avant de renvoyer une demande qui contient un certificat client, vous pouvez déterminer si le niveau de chiffrement pris en charge est acceptable pour votre application. Appelez WinHttpQueryOption et spécifiez l’indicateur WINHTTP_OPTION_SECURITY_FLAGS pour déterminer le niveau de chiffrement utilisé.
Récupération de liste d’émetteurs pour l’authentification client SSL
Lorsque l’application cliente WinHttp envoie une demande à un serveur HTTP sécurisé qui nécessite l’authentification du client SSL, WinHttp retourne un ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED si l’application n’a pas fourni de certificat client. Pour les ordinateurs s’exécutant sur Windows Server 2008 et Windows Vista, WinHttp permet à l’application de récupérer la liste de l’émetteur de certificat fournie par le serveur dans le défi d’authentification. La liste des émetteurs spécifie une liste d’autorités de certification autorisées par le serveur à émettre des certificats clients. L’application filtre la liste de l’émetteur pour obtenir le certificat requis.
L’application cliente WinHttp récupère la liste des émetteurs lorsque WinHttpSendRequest, ou WinHttpReceiveResponse retourne ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. Lorsque cette erreur est retournée, l’application appelle WinHttpQueryOption avec l’option WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST. Le paramètre lpBuffer doit être suffisamment grand pour contenir un pointeur vers la structure SecPkgContext_IssuerListInfoEx. L’exemple de code suivant montre comment récupérer la liste de l’émetteur.
#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.
}
}
Les informations de la structure SecPkgContext_IssuerListInfoEx, cIssuers et aIssuers, peuvent être utilisées pour rechercher le certificat, comme illustré dans l’exemple de code ci-dessous. Pour plus d’informations, consultez 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;
}
Certificats SSL client facultatifs
À compter de Windows Server 2008 et Windows Vista, l’API WinHttp prend en charge les certificats clients facultatifs. Lorsque le serveur demande un certificat client, WinHttpSendRequest, ou WinHttpRecieveResponse retourne une erreur ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. Si le serveur demande le certificat, mais ne le nécessite pas, l’application peut spécifier cette option pour indiquer qu’elle n’a pas de certificat. Le serveur peut choisir un autre schéma d’authentification ou autoriser l’accès anonyme au serveur. L’application spécifie la macro WINHTTP_NO_CLIENT_CERT_CONTEXT dans le paramètre lpBuffer de WinHttpSetOption, comme illustré dans l’exemple de code suivant.
BOOL fRet = WinHttpSetOption ( hRequest,
WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
WINHTTP_NO_CLIENT_CERT_CONTEXT,
0);
Si le WINHTTP_NO_CLIENT_CERT_CONTEXT est défini et que le serveur requiert toujours un certificat client, il peut envoyer un code d’état HTTP 403. Pour plus d’informations, consultez l’option WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST.
WinHttpRequest, objet
Utilisez la méthode SetClientCertificate de l’objet WinHttpRequest pour sélectionner des certificats clients à envoyer au serveur avec une demande. Sélectionnez un certificat en spécifiant une chaîne de sélection de certificat avec la méthode SetClientCertificate. La chaîne de sélection de certificat se compose de l’emplacement du certificat, magasin de certificatset du nom de l’objet délimité par des barres obliques inverses. Le tableau suivant répertorie les composants de cette chaîne de sélection.
Composant | Description | Valeurs possibles |
---|---|---|
Emplacement | Détermine la clé de Registre sous laquelle les certificats sont stockés. | Les valeurs possibles sont « LOCAL_MACHINE » pour indiquer que le magasin de certificats se trouve sous HKEY_LOCAL_MACHINE et « CURRENT_USER » pour indiquer que le magasin de certificats est sous leHKEY_CURRENT_USER non emprunt d’identité. Ce composant respecte la casse. |
magasin de certificats | Indique le nom du magasin de certificats qui contient le certificat approprié. | Les magasins de certificats standard sont « MY », « Root » et « TrustedPeople ». Ce composant respecte la casse. |
Nom de l’objet | Identifie un certificat dans le magasin de certificats spécifié. Le premier certificat qui contient la chaîne spécifiée pour ce composant est sélectionné. | Le nom de l’objet peut être n’importe quelle chaîne. Une chaîne vide indique que le premier certificat du magasin de certificats doit être utilisé. Ce composant ne respecte pas la casse. |
Le magasin de certificats nom et emplacement sont des composants facultatifs. Toutefois, si vous spécifiez un magasin de certificats , vous devez également spécifier l’emplacement de ce magasin de certificats . L’emplacement par défaut est CURRENT_USER et le magasin de certificats par défaut est « MY ».
L’exemple de code suivant montre comment spécifier qu’un certificat avec l’objet « Mon certificat Middle-Tier » doit être choisi dans le magasin de certificats « Personnel » magasin de certificats dans le Registre sous HKEY_LOCAL_MACHINE.
HttpReq.SetClientCertificate("LOCAL_MACHINE\Personal\My Middle-Tier Certificate")
Note
Dans certaines langues, la barre oblique inverse est un caractère d’échappement. N’oubliez pas de modifier la chaîne de sélection de certificat pour tenir compte de ce problème. Par exemple, dans Microsoft JScript, utilisez deux barres obliques inverses adjacentes au lieu d’une.
Si vous ne spécifiez pas de certificat et qu’un serveur HTTPS nécessite un certificat client, WinHTTP sélectionne le premier certificat dans le magasin de certificats par défaut magasin de certificats. Si aucun certificat n’existe, une erreur est générée. Si le certificat n’est pas accepté, le serveur retourne un code d’état 403 pour indiquer que la demande ne peut pas être remplie. Vous pouvez ensuite choisir un certificat plus approprié avec SetClientCertificate et renvoyer la requête.