SSL i WinHTTP
Microsoft Windows HTTP Services (WinHTTP) stöder SSL-transaktioner (Secure Sockets Layer), inklusive klientcertifikat. Det här avsnittet beskriver begrepp som ingår i en SSL-transaktion och hur de hanteras med WinHTTP.
Secure Sockets Layer
SSL är en etablerad standard för att säkerställa säkra HTTP-transaktioner. SSL tillhandahåller en mekanism för att utföra upp till 128-bitars kryptering på alla transaktioner mellan klienten och servern. Det gör att klienten kan verifiera att servern tillhör en betrodd entitet med hjälp av servercertifikat. Det gör också att servern kan bekräfta klientens identitet med klientcertifikat.
Vart och ett av dessa problem med kryptering, serveridentitet och klientidentitet förhandlas i SSL-handskakningen som inträffar när en klient först begär en resurs från en HTTPS-server (Secure Hypertext Transfer Protocol). I princip presenterar klienten och servern var och en lista över nödvändiga och önskade inställningar. Om en gemensam uppsättning krav kan överenskommas och uppfyllas upprättas en SSL-anslutning.
WinHTTP tillhandahåller ett högnivågränssnitt för att använda SSL. Även om informationen om SSL-handskakningen och transaktionen hanteras internt kan du med WinHTTP hämta krypteringsnivåer, ange säkerhetsprotokollet och interagera med server- och klientcertifikat. Följande avsnitt innehåller information om hur du skapar WinHTTP-baserade program som väljer en SSL-protokollversion, undersöker servercertifikat och väljer klientcertifikat som ska skickas till HTTPS-servrar.
Servercertifikat
Servercertifikat skickas från servern till klienten så att klienten kan hämta en offentlig nyckel för servern och se till att servern har verifierats av en certifikatutfärdare. Certifikat kan innehålla olika typer av data. Till exempel innehåller ett X.509-certifikat formatet för certifikatet, certifikatets serienummer, algoritmen som används för att signera certifikatet, namnet på certifikatutfärdare (CA) som utfärdade certifikatet, namnet och den offentliga nyckeln för den entitet som begär certifikatet och certifikatutfärdarsignaturen.
När du använder API:et (WinHTTP Application Programming Interface) kan du hämta ett servercertifikat genom att anropa WinHttpQueryOption och ange flaggan WINHTTP_OPTION_SECURITY_CERTIFICATE_STRUCT. Servercertifikatet returneras i en WINHTTP_CERTIFICATE_INFO struktur. Om du föredrar att hämta certifikatkontexten anger du flaggan WINHTTP_OPTION_SERVER_CERT_CONTEXT i stället.
Om ett servercertifikat innehåller fel kan information om felet hämtas i statusåteranropsfunktionen. Meddelandet WINHTTP_CALLBACK_STATUS_SECURE_FAILURE anger ett fel med ett servercertifikat. Parametern lpvStatusInformation innehåller en eller flera detaljerade felflaggor. Mer information finns i WINHTTP_STATUS_CALLBACK.
Klientcertifikat
Under SSL-handskakningen kan servern kräva autentisering. Klienten autentiseras genom att ett giltigt klientcertifikat skickas till servern. Med WinHTTP kan du välja och skicka ett certifikat från ett lokalt certifikatarkiv. I följande avsnitt beskrivs processen som tillhandahåller klientcertifikat när du använder antingen WinHTTP-API:et eller WinHttpRequest--objektet.
WinHTTP API
Både WinHttpSendRequest och WinHttpReceiveResponse kan misslyckas med att ange att en begäran misslyckades eftersom HTTPS-servern kräver autentisering. I dessa fall anropar du GetLastError för att returnera ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. När du får det här felet använder du lämpliga CryptoAPI- funktioner för att hitta ett lämpligt certifikat. Ange att certifikatet ska skickas med nästa begäran genom att anropa WinHttpSetOption med flaggan WINHTTP_OPTION_CLIENT_CERT_CONTEXT.
I följande kodexempel visas hur du öppnar ett certifikatarkiv och letar upp ett certifikat baserat på ämnesnamnet när ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED-felet har returnerats.
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.
}
}
}
Innan du skickar en begäran som innehåller ett klientcertifikat igen kan du avgöra om krypteringsnivån som stöds är acceptabel för ditt program. Anropa WinHttpQueryOption och ange flaggan WINHTTP_OPTION_SECURITY_FLAGS för att fastställa vilken krypteringsnivå som används.
Hämtning av utfärdarlista för SSL-klientautentisering
När WinHttp-klientprogrammet skickar en begäran till en säker HTTP-server som kräver SSL-klientautentisering returnerar WinHttp en ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED om programmet inte har angett något klientcertifikat. För datorer som körs på Windows Server 2008 och Windows Vista gör WinHttp det möjligt för programmet att hämta listan över certifikatutfärdare som tillhandahålls av servern i autentiseringsuppgiften. Utfärdarlistan anger en lista över certifikatutfärdare som har behörighet av servern att utfärda klientcertifikat. Programmet filtrerar utfärdarlistan för att hämta det certifikat som krävs.
WinHttp-klientprogrammet hämtar utfärdarlistan när WinHttpSendRequesteller WinHttpReceiveResponse returnerar ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED. När det här felet returneras anropar programmet WinHttpQueryOption med alternativet WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST. Parametern lpBuffer måste vara tillräckligt stor för att innehålla en pekare till SecPkgContext_IssuerListInfoEx struktur. I följande kodexempel visas hur du hämtar utfärdarlistan.
#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.
}
}
Informationen i SecPkgContext_IssuerListInfoEx struktur, cIssuers och aIssuers, kan användas för att söka efter certifikatet enligt kodexemplet nedan. Mer information finns i 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;
}
Valfria klient-SSL-certifikat
Från och med Windows Server 2008 och Windows Vista har WinHttp-API:et stöd för valfria klientcertifikat. När servern begär ett klientcertifikat returnerar WinHttpSendRequesteller WinHttpRecieveResponse ett ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED fel. Om servern begär certifikatet, men inte kräver det, kan programmet ange det här alternativet för att ange att det inte har något certifikat. Servern kan välja ett annat autentiseringsschema eller tillåta anonym åtkomst till servern. Programmet anger WINHTTP_NO_CLIENT_CERT_CONTEXT makro i parametern lpBuffer för WinHttpSetOption enligt följande kodexempel.
BOOL fRet = WinHttpSetOption ( hRequest,
WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
WINHTTP_NO_CLIENT_CERT_CONTEXT,
0);
Om WINHTTP_NO_CLIENT_CERT_CONTEXT har angetts och servern fortfarande kräver ett klientcertifikat kan den skicka en 403 HTTP-statuskod. Mer information finns i alternativet WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST.
WinHttpRequest-objekt
Använd metoden SetClientCertificate i WinHttpRequest-objektet för att välja klientcertifikat som ska skickas till servern med en begäran. Välj ett certifikat genom att ange en valsträng för certifikat med metoden SetClientCertificate. Certifikatvalssträngen består av certifikatplatsen, certifikatarkivetoch ämnesnamnet avgränsat med omvänt snedstreck. I följande tabell visas komponenter för den här markeringssträngen.
Komponent | Beskrivning | Möjliga värden |
---|---|---|
Plats | Avgör registernyckeln under vilken certifikaten lagras. | Möjliga värden är "LOCAL_MACHINE" för att indikera att certifikatarkivet är under HKEY_LOCAL_MACHINE och "CURRENT_USER" för att indikera att det certifikatarkivet är under den icke-personifieradeHKEY_CURRENT_USER. Den här komponenten är skiftlägeskänslig. |
certifikatarkiv | Anger namnet på det certifikatarkivet som innehåller det relevanta certifikatet. | Typiska certifikatarkiv är "MY", "Root" och "TrustedPeople". Den här komponenten är skiftlägeskänslig. |
Ämnesnamn | Identifierar ett certifikat i det angivna certifikatarkivet. Det första certifikatet som innehåller strängen som angetts för den här komponenten är markerat. | Ämnesnamnet kan vara valfri sträng. En tom sträng anger att det första certifikatet i certifikatarkivet ska användas. Den här komponenten är skiftlägeskänslig. |
Det certifikatarkivet namn och plats är valfria komponenter. Men om du anger ett certifikatarkivmåste du också ange platsen för det certifikatarkivet. Standardplatsen är CURRENT_USER och standardplatsen för certifikatarkiv är "MY".
I följande kodexempel visas hur du anger att ett certifikat med ämnet "Mitt Middle-Tier certifikat" ska väljas från certifikatarkivet "Personligt" i registret under HKEY_LOCAL_MACHINE.
HttpReq.SetClientCertificate("LOCAL_MACHINE\Personal\My Middle-Tier Certificate")
Not
På vissa språk är omvänt snedstreck ett escape-tecken. Kom ihåg att ändra strängen för val av certifikat för att ta hänsyn till detta. I Microsoft JScript använder du till exempel två intilliggande omvänt snedstreck i stället för ett.
Om du inte anger ett certifikat och en HTTPS-server kräver ett klientcertifikat väljer WinHTTP det första certifikatet i standardarkivet certifikatarkivet. Om det inte finns några certifikat utlöses ett fel. Om certifikatet inte godkänns returnerar servern en 403-statuskod som anger att begäran inte kan uppfyllas. Du kan sedan välja ett lämpligare certifikat med SetClientCertificate och skicka begäran igen.