SSL dans WinHTTP
Microsoft Windows HTTP Services (WinHTTP) prend 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 la façon dont ils sont gérés à l’aide de WinHTTP.
protocole SSL
SSL est une norme établie pour garantir la sécurisation des transactions HTTP. SSL fournit un mécanisme permettant d’effectuer un chiffrement jusqu’à 128 bits 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 client.
Chacun de ces problèmes de chiffrement, d’identité du serveur et d’identité client est négocié dans la négociation SSL qui se produit lorsqu’un client demande pour la première fois une ressource à un serveur HTTPS (Secure Hypertext Transfer Protocol). Essentiellement, le client et le serveur présentent chacun une liste de paramètres obligatoires et préférés. Si un ensemble commun de conditions peut être convenu et respecté, 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 SSL et de la transaction 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 basées sur WinHTTP qui élisent une version de 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 des types différents 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 (API) WinHTTP, vous pouvez récupérer un certificat de serveur en appelant WinHttpQueryOption et en spécifiant l’indicateur 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, vous pouvez obtenir des détails sur l’erreur dans la fonction de rappel status. 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 une 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 client lors de l’utilisation de l’API WinHTTP ou de l’objet WinHttpRequest .
WinHTTP API
WinHttpSendRequest et WinHttpReceiveResponse peuvent ne pas indiquer qu’une demande a échoué, car le serveur HTTPS nécessite une authentification. Dans ce cas, appelez GetLastError pour retourner ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. Une fois cette erreur reçue, utilisez les fonctions 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 WINHTTP_OPTION_CLIENT_CERT_CONTEXT .
L’exemple de code suivant montre comment ouvrir un magasin de certificats et localiser un certificat basé sur le nom de l’objet après le retour de l’erreur 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.
}
}
}
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 la liste des émetteurs pour l’authentification du client SSL
Lorsque l’application cliente WinHttp envoie une requête à 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 des émetteurs de certificats 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 des émetteurs 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 des émetteurs.
#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 contenues dans la structure SecPkgContext_IssuerListInfoEx , cIssuers et aIssuers, peuvent être utilisées pour rechercher le certificat, comme indiqué 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 l’exige 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 a toujours besoin d’un certificat client, il peut envoyer un code de status HTTP 403. Pour plus d’informations, consultez l’option WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST .
Objet WinHttpRequest
Utilisez la méthode SetClientCertificate de l’objet WinHttpRequest pour sélectionner les certificats clients à envoyer au serveur avec une requête. 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, du magasin de certificats et du nom de l’objet délimités 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 est sous HKEY_LOCAL_MACHINE et « CURRENT_USER » pour indiquer que le magasin de certificats se trouve sous le HKEY_CURRENT_USER non usurpé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 classiques sont « MY », « Root » et « TrustedPeople ». Ce composant respecte la casse. |
Nom d'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 nom et l’emplacement du magasin de certificats 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 » dans le Registre sous HKEY_LOCAL_MACHINE.
HttpReq.SetClientCertificate("LOCAL_MACHINE\Personal\My Middle-Tier Certificate")
Notes
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 en tenir compte. Par exemple, dans Microsoft JScript, utilisez deux barres obliques inverses adjacentes au lieu d’une seule.
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. Si aucun certificat n’existe, une erreur est générée. Si le certificat n’est pas accepté, le serveur retourne un code 403 status pour indiquer que la demande ne peut pas être exécutée. Vous pouvez ensuite choisir un certificat plus approprié avec SetClientCertificate et renvoyer la demande.