2012-05-17 14 views
15

Attualmente sto implementando un piccolo server HTTP utilizzando Microsoft HTTP Server API Versione 2.0 (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364510(v=vs.85).aspx).API Microsoft HTTP Server - utilizzando SSL, come richiedere il certificato client?

Devo abilitare HTTPS sul lato server e richiedere anche il certificato client quando arrivano richieste client (ho bisogno che il client sia in grado di autenticare il server e il server per autenticare il client e devono comunicare su SSL).

Finora sono stato in grado di abilitare SSL lato server, in modo da posso collegare in modo sicuro alla {} https://127.0.0.1:9999/hello sito, effettuare richieste al server e ricevere le risposte, ma non sono stato in grado di attivare la funzione che richiede anche il certificato client (e lo verifica).

ho detto nel mio codice di applicazione che sto ascoltando "{} https://127.0.0.1:9999/hello" URL (questo era l'URL ho aggiunto al gruppo di URL) e poi ho usato strumento Netsh.exe di impegnare la porta 9999 per SSL:

C:\>netsh http add sslcert ipport=0.0.0.0:9999 certhash=e515b6512e92f4663252eac72c28a784f2d78c6 appid={2C565242-B238-11D3-442D-0008C779D776} clientcertnegotiation=enable 

Non sono sicuro di cosa dovrebbe fare esattamente questo "clientcertnegotiation = enable", i documenti hanno detto che dovrebbe "attivare la negoziazione del certificato". Così ora ho aggiunto una chiamata di funzione aggiuntiva al mio codice HTTP Server:

DWORD answer = 0; 
    HTTP_SSL_CLIENT_CERT_INFO sslClientCertInfo; 
    ULONG bytesReceived; 
    answer = HttpReceiveClientCertificate(hReqQueue, pRequest->ConnectionId, 0, 
     &sslClientCertInfo, sizeof(HTTP_SSL_CLIENT_CERT_INFO), &bytesReceived, NULL); 

ho capito che ora il client deve essere richiesto per il certificato, ma non funziona (probabilmente sto facendo qualcosa di sbagliato, in modo che sia il motivo per cui sto scrivendo la mia domanda qui). Il valore di "risposta" è 1168 (ERROR_NOT_FOUND). Sto usando il browser firefox come client e ho aggiunto un certificato lì: Strumenti-> Opzioni-> Visualizza certificati-> Importa, quindi firefox dovrebbe probabilmente usare quel certificato o prompt per qualche certificato, forse, ma sospetto che il firefox non funzioni ricevere la richiesta del server per il certificato del cliente.

A questo punto il server HTTP deve chiedere il certificato client in ogni caso? Ho pensato che dovrebbe essere giusto dopo la richiesta in arrivo. Per dimostrare che cosa esattamente sto facendo, sto usando il codice di applicazione di esempio HTTP Server da Microsoft (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364640(v=vs.85).aspx), che ho sligthly modificato:

#include "precomp.h" 
#include <iostream> 

// 
// Macros. 
// 
#define INITIALIZE_HTTP_RESPONSE(resp, status, reason) \ 
do              \ 
{              \ 
    RtlZeroMemory((resp), sizeof(*(resp)));   \ 
    (resp)->StatusCode = (status);      \ 
    (resp)->pReason = (reason);       \ 
    (resp)->ReasonLength = (USHORT) strlen(reason);  \ 
} while (FALSE) 

#define ADD_KNOWN_HEADER(Response, HeaderId, RawValue)    \ 
do                \ 
{                \ 
    (Response).Headers.KnownHeaders[(HeaderId)].pRawValue =  \ 
                 (RawValue);\ 
    (Response).Headers.KnownHeaders[(HeaderId)].RawValueLength = \ 
     (USHORT) strlen(RawValue);        \ 
} while(FALSE) 

#define ALLOC_MEM(cb) HeapAlloc(GetProcessHeap(), 0, (cb)) 

#define FREE_MEM(ptr) HeapFree(GetProcessHeap(), 0, (ptr)) 

// 
// Prototypes. 
// 
DWORD DoReceiveRequests(HANDLE hReqQueue); 

DWORD SendHttpResponse(HANDLE hReqQueue, PHTTP_REQUEST pRequest, USHORT StatusCode, PSTR pReason, PSTR pEntity); 

DWORD SendHttpPostResponse(HANDLE hReqQueue, PHTTP_REQUEST pRequest); 

/*******************************************************************++ 

Routine Description: 
main routine 

Arguments: 
argc - # of command line arguments. 
argv - Arguments. 

Return Value: 
Success/Failure 

--*******************************************************************/ 
int __cdecl wmain(int argc, wchar_t * argv[]) 
{ 
ULONG   retCode; 
HANDLE   hReqQueue  = NULL; //request queue handle 
int    UrlAdded  = 0; 
HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_2; 


retCode = HttpInitialize( 
      HttpApiVersion, 
      HTTP_INITIALIZE_SERVER , 
      NULL      
      ); 

if (retCode == NO_ERROR) 
{ 
    // If intialize succeeded, create server session 
    HTTP_SERVER_SESSION_ID serverSessionId = NULL; 
    retCode = HttpCreateServerSession(HttpApiVersion, &serverSessionId, 0); 
    if (retCode == NO_ERROR) 
    { 
    // server session creation succeeded 

    //create request queue 
    retCode = HttpCreateRequestQueue(HttpApiVersion, NULL, NULL, 0, &hReqQueue); 
    if (retCode == NO_ERROR) 
    { 
     //create the URL group 
     HTTP_URL_GROUP_ID urlGroupId = NULL; 
     retCode = HttpCreateUrlGroup(serverSessionId, &urlGroupId, 0); 
     if (retCode == NO_ERROR) 
     { 
     retCode = HttpAddUrlToUrlGroup(urlGroupId, L"https://127.0.0.1:9999/hello", 0, 0); 
     if (retCode == NO_ERROR) 
     { 
      //Set url group properties 

      //First let's set the binding property: 
      HTTP_BINDING_INFO bindingInfo; 
      bindingInfo.RequestQueueHandle = hReqQueue; 
      HTTP_PROPERTY_FLAGS propertyFlags; 
      propertyFlags.Present = 1; 
      bindingInfo.Flags = propertyFlags; 
      retCode = HttpSetUrlGroupProperty(
        urlGroupId, 
        HttpServerBindingProperty, 
        &bindingInfo, 
         sizeof(HTTP_BINDING_INFO)); 


      DoReceiveRequests(hReqQueue); 
     } 

     HttpCloseUrlGroup(urlGroupId); 
     }//if HttpCreateUrlGroup succeeded 

     HttpCloseRequestQueue(hReqQueue); 
    }//if HttpCreateRequestQueue succeeded 


    HttpCloseServerSession(serverSessionId);   
    } // if HttpCreateServerSession succeeded 

    HttpTerminate(HTTP_INITIALIZE_SERVER, NULL); 
}// if httpInialize succeeded 

return retCode; 

}//main 


/*******************************************************************++ 

Routine Description: 
The function to receive a request. This function calls the 
corresponding function to handle the response. 

Arguments: 
hReqQueue - Handle to the request queue 

Return Value: 
Success/Failure. 

--*******************************************************************/ 
DWORD DoReceiveRequests(IN HANDLE hReqQueue) 
{ 
ULONG    result; 
HTTP_REQUEST_ID requestId; 
DWORD    bytesRead; 
PHTTP_REQUEST  pRequest; 
PCHAR    pRequestBuffer; 
ULONG    RequestBufferLength; 

// 
// Allocate a 2 KB buffer. This size should work for most 
// requests. The buffer size can be increased if required. Space 
// is also required for an HTTP_REQUEST structure. 
// 
RequestBufferLength = sizeof(HTTP_REQUEST) + 2048; 
pRequestBuffer  = (PCHAR) ALLOC_MEM(RequestBufferLength); 

if (pRequestBuffer == NULL) 
{ 
    return ERROR_NOT_ENOUGH_MEMORY; 
} 

pRequest = (PHTTP_REQUEST)pRequestBuffer; 

// 
// Wait for a new request. This is indicated by a NULL 
// request ID. 
// 

HTTP_SET_NULL_ID(&requestId); 

for(;;) 
{ 
    RtlZeroMemory(pRequest, RequestBufferLength); 

    result = HttpReceiveHttpRequest(
       hReqQueue,   // Req Queue 
       requestId,   // Req ID 
       0,     // Flags 
       pRequest,   // HTTP request buffer 
       RequestBufferLength,// req buffer length 
       &bytesRead,   // bytes received 
       NULL    // LPOVERLAPPED 
       ); 
      if(NO_ERROR == result) 
    { 

     DWORD answer = 0; 
     HTTP_SSL_CLIENT_CERT_INFO sslClientCertInfo; 
     ULONG bytesReceived; 
     answer = HttpReceiveClientCertificate(hReqQueue, pRequest->ConnectionId, 0, 
       &sslClientCertInfo, sizeof(HTTP_SSL_CLIENT_CERT_INFO), &bytesReceived, NULL); 


     if (answer != NO_ERROR) 
     { 
      result = SendHttpResponse(hReqQueue, pRequest, 401, "Unauthorized request", "Unauthorized request"); 
     } 
     else 
     { 
      result = SendHttpResponse(hReqQueue, pRequest, 200, "OK", "OK"); 
     } 

     if (result != NO_ERROR) 
     { 
      break; //if failed to send response, stop listening for further incoming requests 
     } 
     // 
     // Reset the Request ID to handle the next request. 
     // 
     HTTP_SET_NULL_ID(&requestId); 
    } 
    else 
    { 
     break; 
    } 

} 
if(pRequestBuffer) 
{ 
    FREE_MEM(pRequestBuffer); 
} 

return result; 
} 



/*******************************************************************++ 

Routine Description: 
The routine sends a HTTP response 

Arguments: 
hReqQueue  - Handle to the request queue 
pRequest  - The parsed HTTP request 
StatusCode - Response Status Code 
pReason  - Response reason phrase 
pEntityString - Response entity body 

Return Value: 
Success/Failure. 
--*******************************************************************/ 

DWORD SendHttpResponse(
IN HANDLE  hReqQueue, 
IN PHTTP_REQUEST pRequest, 
IN USHORT  StatusCode, 
IN PSTR   pReason, 
IN PSTR   pEntityString 
) 
{ 
HTTP_RESPONSE response; 
HTTP_DATA_CHUNK dataChunk; 
DWORD   result; 
DWORD   bytesSent; 


INITIALIZE_HTTP_RESPONSE(&response, StatusCode, pReason); 
ADD_KNOWN_HEADER(response, HttpHeaderContentType, "text/html"); 


if(pEntityString) 
{ 
    // 
    // Add an entity chunk. 
    // 
    dataChunk.DataChunkType   = HttpDataChunkFromMemory; 
    dataChunk.FromMemory.pBuffer  = pEntityString; 
    dataChunk.FromMemory.BufferLength = 
            (ULONG) strlen(pEntityString); 

    response.EntityChunkCount   = 1; 
    response.pEntityChunks   = &dataChunk; 
} 

result = HttpSendHttpResponse(
       hReqQueue,   // ReqQueueHandle 
       pRequest->RequestId, // Request ID 
       0,     // Flags 
       &response,   // HTTP response 
       NULL,    // pReserved1 
       &bytesSent,   // bytes sent (OPTIONAL) 
       NULL,    // pReserved2 (must be NULL) 
       0,     // Reserved3 (must be 0) 
       NULL,    // LPOVERLAPPED(OPTIONAL) 
       NULL     // pReserved4 (must be NULL) 
       ); 

if(result != NO_ERROR) 
{ 
    wprintf(L"HttpSendHttpResponse failed with %lu \n", result); 
} 

return result; 
} 

Quindi la mia domanda è, come potrebbe abilita la funzione che richiede un certificato client e come posso verificare il certificato una volta ricevuto (il codice di esempio corrente tenta solo di ricevere il certificato dal client, manca la parte di verifica)? Non ho trovato alcun campione da Internet che utilizza API Microsoft HTTP Server e richiede certificati client.

Grazie a tutti già in anticipo.

+0

Hi liismai, hai fatto alcun progresso ? Saluti, Manuel – Manuel

risposta

1

La HTTP_SERVICE_CONFIG_SSL_PARAM struttura, che è passato in definitiva a HttpSetServiceConfiguration viene utilizzato per consentire la negoziazione di certificati client (attraverso il bandiera HTTP_SERVICE_CONFIG_SSL_FLAG_NEGOTIATE_CLIENT_CERT) e la procedura di verifica di default (tramite DefaultCertCheckMode).

È possibile recuperare il certificato per eseguire manualmente ulteriori verifiche chiamando HttpReceiveClientCertificate.

Ci sono un paio di esempi di buona qualità, ma molti sembrano chiamare da .net. La configurazione è mostrata in un esempio su p/Invoke page, rimozione di.testa netta è lasciata come esercizio per il lettore:

HTTPAPI_VERSION httpApiVersion = new HTTPAPI_VERSION(1, 0); 
retVal = HttpInitialize(httpApiVersion, HTTP_INITIALIZE_CONFIG, IntPtr.Zero); 
if ((uint)NOERROR == retVal) 
{ 
HTTP_SERVICE_CONFIG_SSL_SET configSslSet = new HTTP_SERVICE_CONFIG_SSL_SET(); 
HTTP_SERVICE_CONFIG_SSL_KEY httpServiceConfigSslKey = new HTTP_SERVICE_CONFIG_SSL_KEY(); 
HTTP_SERVICE_CONFIG_SSL_PARAM configSslParam = new HTTP_SERVICE_CONFIG_SSL_PARAM(); 

IPAddress ip = IPAddress.Parse(ipAddress); 

IPEndPoint ipEndPoint = new IPEndPoint(ip, port); 
// serialize the endpoint to a SocketAddress and create an array to hold the values. Pin the array. 
SocketAddress socketAddress = ipEndPoint.Serialize(); 
byte[] socketBytes = new byte[socketAddress.Size]; 
GCHandle handleSocketAddress = GCHandle.Alloc(socketBytes, GCHandleType.Pinned); 
// Should copy the first 16 bytes (the SocketAddress has a 32 byte buffer, the size will only be 16, 
//which is what the SOCKADDR accepts 
for (int i = 0; i < socketAddress.Size; ++i) 
{ 
    socketBytes[i] = socketAddress[i]; 
} 

httpServiceConfigSslKey.pIpPort = handleSocketAddress.AddrOfPinnedObject(); 

GCHandle handleHash = GCHandle.Alloc(hash, GCHandleType.Pinned); 
configSslParam.AppId = Guid.NewGuid(); 
configSslParam.DefaultCertCheckMode = 0; 
configSslParam.DefaultFlags = HTTP_SERVICE_CONFIG_SSL_FLAG_NEGOTIATE_CLIENT_CERT; 
configSslParam.DefaultRevocationFreshnessTime = 0; 
configSslParam.DefaultRevocationUrlRetrievalTimeout = 0; 
configSslParam.pSslCertStoreName = StoreName.My.ToString(); 
configSslParam.pSslHash = handleHash.AddrOfPinnedObject(); 
configSslParam.SslHashLength = hash.Length; 
configSslSet.ParamDesc = configSslParam; 
configSslSet.KeyDesc = httpServiceConfigSslKey; 

IntPtr pInputConfigInfo = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(HTTP_SERVICE_CONFIG_SSL_SET))); 
Marshal.StructureToPtr(configSslSet, pInputConfigInfo, false); 

retVal = HttpSetServiceConfiguration(IntPtr.Zero, 
    HTTP_SERVICE_CONFIG_ID.HttpServiceConfigSSLCertInfo, 
    pInputConfigInfo, 
    Marshal.SizeOf(configSslSet), 
    IntPtr.Zero); 

if ((uint)ERROR_ALREADY_EXISTS == retVal) // ERROR_ALREADY_EXISTS = 183 
{ 
    retVal = HttpDeleteServiceConfiguration(IntPtr.Zero, 
    HTTP_SERVICE_CONFIG_ID.HttpServiceConfigSSLCertInfo, 
    pInputConfigInfo, 
    Marshal.SizeOf(configSslSet), 
    IntPtr.Zero); 

    if ((uint)NOERROR == retVal) 
    { 
    retVal = HttpSetServiceConfiguration(IntPtr.Zero, 
     HTTP_SERVICE_CONFIG_ID.HttpServiceConfigSSLCertInfo, 
     pInputConfigInfo, 
     Marshal.SizeOf(configSslSet), 
     IntPtr.Zero); 
    } 
} 

C'è un separate pastebin che utilizza netsh a fare la configurazione, ma non accedere al certificato ricevuto:

for(;;) 
{ 
    RtlZeroMemory(pRequest, RequestBufferLength); 

    result = HttpReceiveHttpRequest(
       hReqQueue,   // Req Queue 
       requestId,   // Req ID 
       0,     // Flags 
       pRequest,   // HTTP request buffer 
       RequestBufferLength,// req buffer length 
       &bytesRead,   // bytes received 
       NULL    // LPOVERLAPPED 
       ); 
      if(NO_ERROR == result) 
    { 

     DWORD answer = 0; 
     HTTP_SSL_CLIENT_CERT_INFO sslClientCertInfo; 
     ULONG bytesReceived; 
     answer = HttpReceiveClientCertificate(hReqQueue, pRequest->ConnectionId, 0, 
       &sslClientCertInfo, sizeof(HTTP_SSL_CLIENT_CERT_INFO), &bytesReceived, NULL); 


     if (answer != NO_ERROR) 
     { 
      result = SendHttpResponse(hReqQueue, pRequest, 401, "Unauthorized request", "Unauthorized request"); 
     } 
     else 
     { 
      result = SendHttpResponse(hReqQueue, pRequest, 200, "OK", "OK"); 
     } 

     if (result != NO_ERROR) 
     { 
      break; //if failed to send response, stop listening for further incoming requests 
     } 
     // 
     // Reset the Request ID to handle the next request. 
     // 
     HTTP_SET_NULL_ID(&requestId); 
    } 
    else 
    { 
     break; 
    } 

} 
Problemi correlati