Tôi hiện đang triển khai một máy chủ HTTP nhỏ sử dụng Microsoft HTTP Server API Phiên bản 2.0 (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364510(v=vs.85).aspx).Microsoft HTTP Server API - sử dụng SSL, cách yêu cầu chứng chỉ ứng dụng khách?
Tôi cần bật HTTPS ở phía máy chủ và cũng yêu cầu chứng chỉ ứng dụng khách khi yêu cầu của khách hàng đến (tôi cần khách hàng có thể xác thực máy chủ và máy chủ để xác thực máy khách và họ nên liên lạc qua SSL).
Cho đến nay tôi đã có thể bật SSL phía máy chủ, vì vậy tôi có thể kết nối an toàn với trang web {https://127.0.0.1:9999/hello}, thực hiện yêu cầu tới máy chủ và nhận phản hồi nhưng tôi chưa thể bật tính năng này cũng yêu cầu chứng chỉ ứng dụng khách (và xác minh nó).
Tôi đã nói trong mã ứng dụng rằng tôi đang nghe "{https://127.0.0.1:9999/hello}" URL (đây là URL tôi đã thêm vào nhóm URL) và sau đó tôi sử dụng công cụ netsh.exe để kết buộc cổng 9999 với SSL:
C:\>netsh http add sslcert ipport=0.0.0.0:9999 certhash=e515b6512e92f4663252eac72c28a784f2d78c6 appid={2C565242-B238-11D3-442D-0008C779D776} clientcertnegotiation=enable
Tôi không chắc chắn điều này "clientcertnegotiation = enable" nên chính xác làm, các tài liệu cho biết cần "bật đàm phán chứng chỉ". Vì vậy, bây giờ tôi thêm một chức năng bổ sung gọi mã máy chủ HTTP của tôi:
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);
tôi hiểu rằng bây giờ là khách hàng nên được nhắc nhở cấp giấy chứng nhận, nhưng nó không hoạt động (tôi có thể làm điều gì đó sai, vì vậy đó là lý do tại sao tôi viết câu hỏi của tôi ở đây). Giá trị của "câu trả lời" là 1168 (ERROR_NOT_FOUND). Tôi đang sử dụng trình duyệt firefox với tư cách máy khách và tôi đã thêm chứng chỉ vào đó: Tools-> Options-> View Certificates-> Import, vì vậy firefox có lẽ nên sử dụng cert hoặc prompt cho một số chứng chỉ, nhưng tôi nghi ngờ rằng firefox doesn không nhận được yêu cầu của máy chủ cho chứng chỉ ứng dụng khách.
Lúc nào máy chủ HTTP cũng nên yêu cầu chứng chỉ ứng dụng khách? Tôi nghĩ rằng nó nên được ngay sau khi yêu cầu đến. Để chứng minh những gì chính xác tôi làm, tôi đang sử dụng HTTP Server mã ứng dụng mẫu từ Microsoft (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364640(v=vs.85).aspx), mà tôi đã sligthly chỉnh sửa:
#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;
}
Vì vậy, câu hỏi của tôi là, làm thế nào tôi có thể kích hoạt tính năng mà yêu cầu chứng chỉ ứng dụng khách và làm cách nào để xác minh chứng chỉ sau khi tôi nhận được chứng chỉ đó (mã mẫu hiện tại chỉ cố gắng nhận chứng chỉ từ ứng dụng khách, phần xác minh bị thiếu)? Tôi thực sự không tìm thấy bất kỳ mẫu nào từ internet sử dụng API Microsoft HTTP Server và yêu cầu chứng chỉ ứng dụng khách.
Cảm ơn tất cả các bạn đã có trước.
Hi liismai, bạn đã thực hiện bất kỳ sự tiến bộ ? Chúc mừng, Manuel – Manuel