2012-05-17 10 views
15

मैं वर्तमान में माइक्रोसॉफ्ट HTTP सर्वर एपीआई संस्करण 2.0 (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364510(v=vs.85).aspx) का उपयोग कर एक छोटा HTTP सर्वर लागू कर रहा हूं।माइक्रोसॉफ्ट HTTP सर्वर एपीआई - एसएसएल का उपयोग, ग्राहक प्रमाण पत्र की मांग कैसे करें?

मुझे सर्वर पक्ष पर HTTPS को सक्षम करने की आवश्यकता है और क्लाइंट अनुरोध आने पर क्लाइंट प्रमाणपत्र की भी आवश्यकता है (मुझे क्लाइंट को प्रमाणित करने के लिए क्लाइंट को प्रमाणित करने के लिए क्लाइंट की आवश्यकता है और क्लाइंट को प्रमाणीकृत करने के लिए सर्वर और उन्हें एसएसएल पर संवाद करना चाहिए)।

अब तक मैं सर्वर-साइड एसएसएल सक्षम करने में सक्षम हूं, इसलिए मैं सुरक्षित रूप से {https://127.0.0.1:9999/hello} साइट से कनेक्ट कर सकता हूं, सर्वर से अनुरोध कर सकता हूं और प्रतिक्रिया प्राप्त कर सकता हूं, लेकिन मैं सुविधा को चालू करने में सक्षम नहीं हूं जो ग्राहक प्रमाण पत्र के साथ-साथ अनुरोध करता है (और इसे सत्यापित करता है)।

मैं अपने आवेदन कोड में कहा कि मैं सुन रहा हूँ "{https://127.0.0.1:9999/hello}" URL (यह URL मैं यूआरएल समूह में जोड़ा था) और फिर मैं SSL के लिए 9999 पोर्ट बाध्य करने के लिए Netsh.exe उपकरण का उपयोग किया:

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

मुझे यकीन नहीं है कि यह "क्लाइंटक्रर्टनेगोएशन = सक्षम" वास्तव में क्या करना चाहिए, दस्तावेज़ों ने कहा कि इसे "प्रमाण पत्र की बातचीत चालू करनी चाहिए"। तो अब मैं अपने HTTP सर्वर कोड को एक अतिरिक्त समारोह कॉल कहा:

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

मैं समझ गया अब क्लाइंट प्रमाणपत्र के लिए संकेत दिया जाना चाहिए कि है, लेकिन यह काम नहीं करता है (मैं शायद, कुछ गलत कर रहा हूँ ताकि है कारण मैं यहां अपना प्रश्न लिख रहा हूं)। "उत्तर" का मान 1168 (ERROR_NOT_FOUND) है। मैं एक क्लाइंट के रूप में फ़ायरफ़ॉक्स ब्राउज़र का उपयोग कर रहा हूं और मैंने वहां एक प्रमाणपत्र जोड़ा है: टूल्स-> विकल्प-> सर्टिफिकेट देखें-> आयात करें, इसलिए फ़ायरफ़ॉक्स शायद उस प्रमाणपत्र का उपयोग कर सकता है या कुछ सर्टिफिकेट के लिए प्रॉम्प्ट शायद, लेकिन मुझे संदेह है कि फ़ायरफ़ॉक्स नहीं करता है क्लाइंट प्रमाण पत्र के लिए सर्वर का अनुरोध बिल्कुल प्राप्त नहीं होता है।

किस बिंदु पर HTTP सर्वर क्लाइंट प्रमाणपत्र के लिए पूछना चाहिए? मैंने सोचा कि आने वाले अनुरोध के बाद यह सही होना चाहिए। प्रदर्शित करने के लिए वास्तव में मैं क्या कर रहा हूँ, मैं Microsoft से HTTP सर्वर नमूना अनुप्रयोग कोड (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364640(v=vs.85).aspx) का उपयोग कर रहा हूँ, कि मैं sligthly संशोधित कर लिया है:

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

तो मेरे सवाल का, मैं कैसे सुविधा को सक्षम कर सकता है कि क्लाइंट सर्टिफिकेट की आवश्यकता है और मुझे प्राप्त होने के बाद मैं प्रमाण पत्र कैसे सत्यापित करूं (वर्तमान नमूना कोड केवल क्लाइंट से प्रमाण पत्र प्राप्त करने का प्रयास करता है, सत्यापन भाग गुम है)? मुझे वास्तव में इंटरनेट से कोई नमूने नहीं मिला है जो Microsoft HTTP सर्वर API का उपयोग करता है और क्लाइंट प्रमाणपत्रों की आवश्यकता होती है।

सभी पहले से ही धन्यवाद।

+0

हाय liismai, आप किसी भी प्रगति की है ? चीयर्स, मैनुअल – Manuel

उत्तर

1

HTTP_SERVICE_CONFIG_SSL_PARAM संरचना, जो HttpSetServiceConfiguration अंततः पारित हो जाता है क्लाइंट प्रमाण पत्र (HTTP_SERVICE_CONFIG_SSL_FLAG_NEGOTIATE_CLIENT_CERT ध्वज के माध्यम से) और डिफ़ॉल्ट सत्यापन चरणों (DefaultCertCheckMode के माध्यम से) की बातचीत सक्षम करने के लिए प्रयोग किया जाता है।

आप HttpReceiveClientCertificate पर कॉल करके मैन्युअल रूप से अतिरिक्त सत्यापन करने के लिए प्रमाण पत्र पुनर्प्राप्त कर सकते हैं।

कुछ अच्छे-इश उदाहरण हैं, लेकिन अधिकांश .net से कॉल करने लगते हैं। कॉन्फ़िगरेशन को p/Invoke page पर हटाने के उदाहरण में दिखाया गया है।शुद्ध भूमि के ऊपर पाठक के लिए एक व्यायाम के रूप में छोड़ दिया जाता है:

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

एक separate pastebin जो netsh का उपयोग करता है विन्यास करने के लिए नहीं है, लेकिन प्राप्त प्रमाण पत्र का उपयोग करता है:

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

} 
संबंधित मुद्दे

 संबंधित मुद्दे