We help IT Professionals succeed at work.

WinHttp - Which SSL client cert to use? - error 12044 (C++)

Jax Logan
Jax Logan asked
on
Experts,

I'm trying to use Microsoft's WinHttp to Post to the server via https. Unfortunately I get a 12044 -
Client Certificate needed. I'm working on Win XP SP 3. I'm not sure how to select the correct client cert for the SSL. I found the sample code for CertFindCertificateInStore below but it's not returning a valid certificate.

Any ideas? Thanks!

#include <stdio.h>
#include <windows.h>
#include <winhttp.h>
#include <Wincrypt.h>
#pragma comment(lib, "crypt32.lib")

//#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "winhttp.lib")

int main(int argc, char *argv[])
{
    //DWORD dwSize = 0;
    DWORD dwDownloaded = 0;
    LPSTR pszOutBuffer;
    BOOL  bResults = FALSE;
    HINTERNET  hSession = NULL, 
               hConnect = NULL,
               hRequest = NULL;
    LPCWSTR acceptTypes[2] = {TEXT("text/xml ; charset=utf-8"), NULL};
    DWORD data;
    DWORD dwSize = sizeof(DWORD);
&#9;LPCWSTR additionalHeaders = L"";
&#9;DWORD hLen   = sizeof(LPCWSTR) * wcslen(additionalHeaders)+1;
&#9;LPVOID dwCert = NULL;
    DWORD dwCertLen = NULL;

&#9;LPSTR  msg = "<Request>12345</Request>";
&#9;//const void* msgToSend = (const void*)msg;
&#9;DWORD data_len = sizeof(LPSTR) * strlen(msg);

    // Use WinHttpOpen to obtain a session handle.
    hSession = WinHttpOpen( L"my user agent",  
                            WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                            WINHTTP_NO_PROXY_NAME, 
                            WINHTTP_NO_PROXY_BYPASS, 0);

        // Use WinHttpQueryOption to retrieve internet options.
        if (WinHttpQueryOption( hSession, 
                                WINHTTP_OPTION_CONNECT_TIMEOUT, 
                                &data, &dwSize))
        {
            printf("Connection timeout: %u ms\n\n",data);
        }
        else
        {
            printf( "Error %u in WinHttpQueryOption.\n", 
                    GetLastError());
        }  

    // Specify an HTTP server.
    if (hSession)
&#9;&#9;hConnect = WinHttpConnect( hSession, L"172.155.155.122",
                                   443, 0);

    // Create an HTTP request handle.
    if (hConnect)
        hRequest = WinHttpOpenRequest( hConnect, L"POST", L"/TEST",
                                       NULL, WINHTTP_NO_REFERER, 
                                       acceptTypes, 
                                       WINHTTP_FLAG_SECURE);



    // Send a request.
    if (hRequest)
        bResults = WinHttpSendRequest( hRequest,
                                       NULL,
                                       0, (LPVOID)msg, data_len, 
                                       data_len, WINHTTP_FLAG_SECURE);

&#9;DWORD dwError = GetLastError();
    if ( dwError == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED )
    {
&#9;&#9;
      //MY is the store the certificate is in.
&#9;PCCERT_CONTEXT  pCertContext = NULL; 
&#9;LPVOID szCertName = NULL;
      HCERTSTORE 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.
&#9;  }
       
    }
    // Send a request.
    if (hRequest)
        bResults = WinHttpSendRequest( hRequest,
                                       WINHTTP_NO_ADDITIONAL_HEADERS,
                                       0, (LPVOID)msg, data_len, 
                                       data_len, WINHTTP_FLAG_SECURE);


&#9;if (!bResults)
        printf( "Error %d has occurred - WinHttpSendRequest.\n", GetLastError());
 
    // End the request.
    if (bResults)
        bResults = WinHttpReceiveResponse( hRequest, NULL);

    // Continue to verify data until there is nothing left.
    if (bResults)
        do 
        {

            // Verify available data.
            dwSize = 0;
            if (!WinHttpQueryDataAvailable( hRequest, &dwSize))
                printf( "Error %u in WinHttpQueryDataAvailable.\n",
                        GetLastError());

            // Allocate space for the buffer.
            pszOutBuffer = new char[dwSize+1];
            if (!pszOutBuffer)
            {
                printf("Out of memory\n");
                dwSize=0;
            }
            else
            {
                // Read the Data.
                ZeroMemory(pszOutBuffer, dwSize+1);

                if (!WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, 
                                      dwSize, &dwDownloaded))
                    printf( "Error %u in WinHttpReadData.\n", GetLastError());
                else
                    printf( "%s\n", pszOutBuffer);
            
                // Free the memory allocated to the buffer.
                delete [] pszOutBuffer;
            }

        } while (dwSize > 0);


    // Report any errors.
    if (!bResults)
        printf( "Error %d has occurred - WinHttpReceiveResponse.\n", GetLastError());

    // Close open handles.
    if (hRequest) WinHttpCloseHandle(hRequest);
    if (hConnect) WinHttpCloseHandle(hConnect);
    if (hSession) WinHttpCloseHandle(hSession);

&#9;system("pause");
   return( 0 );
}

Open in new window

Comment
Watch Question

jkr
Top Expert 2012

Commented:
Have you imported the certificate in question into your store?
I believe the correct sequence is that you call WinHttpQueryOption with WINHTTP_OPTION_CLIENT_CERT_ISSUER_LIST that gives the list of acceptable certificates. Then find the certificate with CERT_FIND_ISSUER_NAME, passing it the issuers (if present). Otherwise If there no certificate then you can try the  WINHTTP_NO_CLIENT_CERT_CONTEXT value in SetOption( WINHTTP_OPTION_CLIENT_CERT_CONTEXT ).
Your code may work too, but I 'm not sure about the NULL subject though. Can try to create a localy signed cert and use its subject value here?

Author

Commented:
ambience, thanks your post.

The problem I'm having is that the Server does not require a client cert but yet I'm getting the '12044 -
Client Certificate needed' error. If I pick any client cert with private key from the store, I get a 'Bad Client cert' from the server.

The other option you mentioned WINHTTP_NO_CLIENT_CERT_CONTEXT is not an option since I'm working on Win XP and it's not supported:
http://msdn.microsoft.com/en-us/library/aa384066%28VS.85%29.aspx

What's a guy to do?
Its hell confusing when it says
"Windows Server 2003 with SP1 and Windows XP with SP2:  The WINHTTP_NO_CLIENT_CERT_CONTEXT macro is not supported."
Two things
- What about XP with SP3?
- It says the "macro is not supported," Can you try passing NULL inplace of WINHTTP_NO_CLIENT_CERT_CONTEXT? (that's what it's defined as)

Author

Commented:
ambience - thanks for the post.

The solution was a combination of two things:
First, I needed to set the not-so-well documented security flags for winhttp below.

The second part was setting WINHTTP_NO_CLIENT_CERT_CONTEXT with flag value 'NULL' as ambience suggested. Without setting the security flags below I get error 12175 - no credentials supplied.

      DWORD dwIgnoreFlags = SECURITY_FLAG_IGNORE_UNKNOWN_CA |
                              SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
                                      SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
                                      SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;

      if (WinHttpSetOption( hRequest, WINHTTP_OPTION_SECURITY_FLAGS, &dwIgnoreFlags,
sizeof(dwIgnoreFlags) ) == TRUE) {
                  printf("WinHttpSetOption ok\n");
      }

Author

Commented:
Please note that the second part of the solution is in my last comment.