1
votes

Microsoft C++ HTTP Server API (httpapi.lib) for HTTPS

I want create https server in c++ by Microsoft http server api http://msdn.microsoft.com/en-us/library/windows/desktop/aa364510(v=vs.85).aspx

i following these steps that are mentioned below

http://www.codeproject.com/Articles/24027/SSL-with-Self-hosted-WCF-Service?msg=3930582#xx3930582xx

  1. makecert -sv SignRoot.pvk -cy authority -r signroot.cer -a sha1 -n "CN=Dev Certification Authority" -ss my -sr localmachine

Succeeded

  1. makecert -iv SignRoot.pvk -ic signroot.cer -cy end -pe -n CN="localhost" -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localmachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12

Succeeded

  1. config https according http://msdn.microsoft.com/en-us/library/ms733791.aspx

netsh http add sslcert ipport=127.0.0.1:4443 certhash=9eb4919ea794d783a348a0469474c4cf45d65fb3 appid={F8C3E640-D91E-11D2-9944-8F3C3FD5C32F} sslctlstorename=MY clientcertnegotiation=enable

SSL Certificate successfully added

Result :

IP:port                 : 127.0.0.1:4443
Certificate Hash        : 9eb4919ea794d783a348a0469474c4cf45d65fb3
Application ID          : {f8c3e640-d91e-11d2-9944-8f3c3fd5c32f} 
Certificate Store Name  : (null) 
Verify Client Certificate Revocation    : Enabled
Verify Revocation Using Cached Client Certificate Only    : Disabled
Usage Check    : Enabled
Revocation Freshness Time : 0 
URL Retrieval Timeout   : 0 
Ctl Identifier          : (null) 
Ctl Store Name          : MY 
DS Mapper Usage    : Disabled
Negotiate Client Certificate    : Enabled

This is complete sample code

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <conio.h>
#include <iostream> 
#include <tchar.h>

using namespace std;

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <http.h>
#pragma comment(lib, "httpapi.lib")

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

/*******************************************************************/

int __cdecl wmain(int argc, wchar_t * argv[])
{   
    HTTPAPI_VERSION HttpApiVersion = HTTPAPI_VERSION_1;

    ULONG ret = NO_ERROR;
    HRESULT hr = S_OK;
    HTTPAPI_VERSION ver = HTTPAPI_VERSION_1;

    ret = HttpInitialize(ver,HTTP_INITIALIZE_SERVER|HTTP_INITIALIZE_CONFIG,NULL);
    if(ret!=NO_ERROR)
        return 0;


    ULONG           retCode;
    HANDLE          hReqQueue      = NULL;  //request queue handle
    int             UrlAdded       = 0;

    retCode = HttpInitialize( 
                HttpApiVersion,
                HTTP_INITIALIZE_SERVER ,
                NULL                     
                );

    if (retCode == NO_ERROR)
    {  
        retCode = HttpCreateHttpHandle(&hReqQueue,// Req Queue
                    0                  // Reserved
                    );   
        if (retCode == NO_ERROR)
        {    
             retCode = HttpAddUrl(
                            hReqQueue,    // Req Queue
                            L"https://127.0.0.1:4443/",// Fully qualified URL
                            NULL          // Reserved
                            );
            if (retCode == NO_ERROR)
            {            
              DoReceiveRequests(hReqQueue);
            }      
        }
    }            
    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;
}

My problem is HttpReceiveClientCertificate Return 1168 ERROR_NOT_FOUND

The function cannot find the client certificate. There is no guarantee that the client certificate will be available even if HTTPCFG is configured with /f 2 [Negotiate Client Cert].

http://msdn.microsoft.com/en-us/library/windows/desktop/aa364494(v=vs.85).aspx

thanks

1
100+ lines of issue and not one question.Aesthete
my question is why I get error 1168 when I recive request from 127.0.0.1:4443Naderi

1 Answers

2
votes

I don't know why you get error 1168. But if your real question was: how do I get the client certificate, then the answer is: there is a copy in your HTTP_REQUEST:

ULONG sz = pRequest->pSslInfo->pClientCertInfo->CertEncodedSize;
unsigned char *ptr = pRequest->pSslInfo->pClientCertInfo->pCertEncoded;
const CERT_CONTEXT * cp = CertCreateCertificateContext(
                PKCS_7_ASN_ENCODING|X509_ASN_ENCODING,ptr,sz);
const CERT_INFO *ci = cp->pCertInfo;
char ibuf[500], sbuf[500];
CertNameToStr(X509_ASN_ENCODING,(PCERT_NAME_BLOB)&ci->Issuer,CERT_SIMPLE_NAME_STR,ibuf,sizeof(ibuf));
CertNameToStr(X509_ASN_ENCODING,(PCERT_NAME_BLOB)&ci->Subject,CERT_SIMPLE_NAME_STR,sbuf,sizeof(sbuf));
//these are FileTime instances...
//ci->NotBefore
//ci->NotAfter
CertFreeCertificateContext(cp);