Click here to Skip to main content
Rate this: bad
good
Please Sign up or sign in to vote.
See more: C++
I need to send a client certificate with a web request (via SSL). This client cert is just a public key. I am trying to replicate the Request.ClientCertificates.Add(Cert); .NET method using C++/WinHTTP. I am loading the .cer file successfully and setting the CERT_CONTEXT via WinHttpSetOption/WINHTTP_OPTION_CLIENT_CERT_CONTEXT. This call succeeds, but when I call WinHttpSendRequest, it fails with ERROR_WINHTTP_CLIENT_CERT_NO_PRIVATE_KEY (12185).

So, the question is, how do I send a client cert public key to the server, as the ClientCertificates.Add method does in .NET? Code snippet sample below:

 
BOOL HTTPCallEx::SendHTTPRequest(int iVerb /*=HTTPCALL_GET*/, LPCTSTR cpUID /*=NULL*/, LPCTSTR cpPWD /*=NULL*/)
 {
   WCHAR wcaVerb[16];
   WCHAR wcaResource[1024];
 
  m_dwLastError = 0;
 
  switch (iVerb)
     {
       case HTTPCALL_POST:
         lstrcpyW(wcaVerb,L"POST");
         break;
 
      case HTTPCALL_HEAD:
         lstrcpyW(wcaVerb,L"HEAD");
         break;
         
      case HTTPCALL_PUT:
         lstrcpyW(wcaVerb,L"PUT");
         break;
 
      case HTTPCALL_DELETE:
         lstrcpyW(wcaVerb,L"DELETE");
         break;
 
      case HTTPCALL_OPTIONS:
         lstrcpyW(wcaVerb,L"OPTIONS");
         break;
 
      case HTTPCALL_TRACE:
         lstrcpyW(wcaVerb,L"TRACE");
         break;
       case HTTPCALL_CONNECT:
         lstrcpyW(wcaVerb,L"CONNECT");
         break;
         
      case HTTPCALL_GET:
       default:
         lstrcpyW(wcaVerb,L"GET");
         break;
     }
 
#ifdef UNICODE
   _tcscpy(wcaResource,m_caResource);
 #else
   MultiByteToWideChar(CP_UTF8,0,m_caResource,-1,wcaResource,1024);
 #endif
 
  m_hRequest = WinHttpOpenRequest(m_hConnect,wcaVerb,wcaResource,NULL,WINHTTP_NO_REFERER,WINHTTP_DEFAULT_ACCEPT_TYPES,(m_bSSL ? WINHTTP_FLAG_SECURE : 0));
 
  if (!m_hRequest)
     {
       m_dwLastError = ::GetLastError();
       return FALSE;
     }
 
  if (cpUID && *cpUID)
     {
       WCHAR wcaUID[512];
       WCHAR wcaPWD[512];
       
#ifdef UNICODE
       _tcscpy(wcaUID,cpUID);
 #else
       MultiByteToWideChar(CP_UTF8,0,cpUID,-1,wcaUID,512);
 #endif
 
      if (cpPWD && *cpPWD)
 #ifdef UNICODE
         _tcscpy(wcaPWD,cpPWD);
 #else
         MultiByteToWideChar(CP_UTF8,0,cpPWD,-1,wcaPWD,512);
 #endif
       else
         wcaPWD[0] = 0;
       
      if (!WinHttpSetCredentials(m_hRequest,
                                  WINHTTP_AUTH_TARGET_SERVER,
                                  WINHTTP_AUTH_SCHEME_BASIC,
                                  wcaUID,
                                  wcaPWD,
                                  NULL))
         {
           m_dwLastError = ::GetLastError();
           return FALSE;
         }
     }
     
  if (m_dwRequestTimeout)
     {
       if (!WinHttpSetOption(m_hRequest,WINHTTP_OPTION_RECEIVE_TIMEOUT,&m_dwRequestTimeout,sizeof(m_dwRequestTimeout)))
         {
           m_dwLastError = ::GetLastError();
           return FALSE;
         }
     }
     
  // Set certificate
   if (m_pCertCtxt)
     {
 
      // disable for now, to get  this working...
 
      DWORD dwFlags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
                       SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
                       SECURITY_FLAG_IGNORE_UNKNOWN_CA |
                       SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
                       
      WinHttpSetOption(m_hRequest,WINHTTP_OPTION_SECURITY_FLAGS,&dwFlags,sizeof(dwFlags));
 
      if (!WinHttpSetOption(m_hRequest,WINHTTP_OPTION_CLIENT_CERT_CONTEXT,(void *)m_pCertCtxt,sizeof(CERT_CONTEXT)))
         {
           if (m_pCertCtxt)
             m_dwLastError = ::GetLastError();
           else
             m_dwLastError = 50000;
             
          return FALSE;
         }
     }
 
 
  if (m_oCustomHeaders.GetSize() > 0)
     {
       CString cHeader;
       WCHAR wcaHeaderBuf[2048];
 
      for (int iLup = 0; iLup < m_oCustomHeaders.GetSize(); iLup++)
         {
           cHeader = m_oCustomHeaders.GetAt(iLup);
 
#ifdef UNICODE
           _tcscpy(wcaHeaderBuf,(LPCTSTR)cHeader);
 #else
           MultiByteToWideChar(CP_UTF8,0,(LPCSTR)cHeader,-1,wcaHeaderBuf,2048);
 #endif
           
          WinHttpAddRequestHeaders(m_hRequest,wcaHeaderBuf,lstrlenW(wcaHeaderBuf),WINHTTP_ADDREQ_FLAG_ADD);
         }
     }
 
 
  DWORD dwContentLength = 0;
   if ((iVerb == HTTPCALL_POST || iVerb == HTTPCALL_PUT) && m_cpPostData)
     {
       if (m_iPostDataLen < 0)
         dwContentLength = (DWORD)strlen(m_cpPostData);
       else
         dwContentLength = (DWORD)m_iPostDataLen;
     }
 
  if (!WinHttpSendRequest(m_hRequest,WINHTTP_NO_ADDITIONAL_HEADERS,NULL,(LPVOID)(m_cpPostData ? m_cpPostData : ""),dwContentLength,dwContentLength,0))
     {
 >>>>> THIS FAILS HERE WITH ERROR_WINHTTP_CLIENT_CERT_NO_PRIVATE_KEY (12185)    
      m_dwLastError = ::GetLastError();
       return FALSE;
     }
 
  if (!WinHttpReceiveResponse(m_hRequest,NULL))
     {
       m_dwLastError = ::GetLastError();
       return FALSE;
     }
 
  TCHAR caBuf[81];
 
  caBuf[0] = 0;
   int iBufSize = sizeof(caBuf)/sizeof(TCHAR);
   if (!GetStandardHeader(WINHTTP_QUERY_STATUS_CODE,caBuf,&iBufSize))
     {
       m_dwLastError = ::GetLastError();
       return FALSE;
     }
 
  m_dwHTTPStatus = _ttol(caBuf);
 
  caBuf[0] = 0;
   iBufSize = sizeof(caBuf)/sizeof(TCHAR);
   if (!GetStandardHeader(WINHTTP_QUERY_CONTENT_LENGTH,caBuf,&iBufSize))  // no content-length is ok, I guess, for chunked transfers...
     m_dwContentLength = 0;
   else
     m_dwContentLength = _ttol(caBuf);
 
  return TRUE;
 }
 
 
As usual, this is on a deadline, so any help is greatly appreciated! Certificate loading is shown below:

 
BOOL LoadCertificate(ApplicationInstance *pAppInst)
 {
   BOOL bRetval = FALSE;
   
  int iThreadCount = (int)pAppInst->m_pLightningServer->m_wNumWorkerThreads;
   TCHAR caCertFilePath[256];
   
  caCertFilePath[0] = 0;
   if (!pAppInst->GetUserTagValue(_T("CertFilePath"),caCertFilePath,sizeof(caCertFilePath)/sizeof(TCHAR)))
     _tcscpy(caCertFilePath,_T("c:\\webapps\\test.cer"));
 
 
  theApp.m_hStore = CertOpenStore(CERT_STORE_PROV_FILENAME,
                                   X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                                   NULL,
                                   CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG,
                                   caCertFilePath);
   if (theApp.m_hStore)
     {
       PCCERT_CONTEXT *pCertList = new (PCCERT_CONTEXT[iThreadCount]);
       if (pCertList)
         {
           for (int iCert = 0; iCert < iThreadCount; iCert++)
             pCertList[iCert] = NULL;
           
          pAppInst->SetUserPtr((void *)pCertList);
 /*
           PCCERT_CONTEXT pCertCtxt = CertFindCertificateInStore(theApp.m_hStore,
                                                                 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                                                                 0,
                                                                 CERT_FIND_ANY,
                                                                 NULL,
                                                                 NULL
                                                                );
 */
  PCCERT_CONTEXT pCertCtxt = CertFindCertificateInStore(theApp.m_hStore,
              X509_ASN_ENCODING,
              0,
              CERT_FIND_SUBJECT_STR,
              (LPVOID)_T("test.myserver.com"), //Subject string in the certificate.
              NULL );
  
           if (pCertCtxt)                        
            {
               pCertList[0] = pCertCtxt;
               for (int iLup = 1; iLup < iThreadCount; iLup++)
                 pCertList[iLup] = CertDuplicateCertificateContext(pCertCtxt);
                 
              bRetval = TRUE;
             }       
          else
             {
               pAppInst->m_pLightningServer->WriteErrorLog(-3,_T("APPINIT: Error Getting CERT_CONTEXT From Store"),caCertFilePath,NULL,FALSE);
             }
         }
       else
         {
           pAppInst->m_pLightningServer->WriteErrorLog(-2,_T("APPINIT: Error MemAlloc CERT_CONTEXT Array"),NULL,NULL,FALSE);
         }
     }
   else
     {
       DWORD dwError = GetLastError();
       TCHAR caErrBuf[1024];
       _stprintf(caErrBuf,_T("APPINIT: Error Opening Cert Store [%d]..."),dwError);
       ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
                       NULL,
                       dwError,
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
                      caErrBuf+_tcslen(caErrBuf),
                       sizeof(caErrBuf)/sizeof(TCHAR)-40,
                       NULL);
       
      pAppInst->m_pLightningServer->WriteErrorLog(-1,caErrBuf,caCertFilePath,NULL,FALSE);
     }
 
  return bRetval;
 }
 
Posted 15-Feb-13 9:25am

1 solution

Rate this: bad
good
Please Sign up or sign in to vote.

Solution 1

Google gives me this tip: http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/72c7ab70-8d24-491e-9e71-e3a457ae39da[^]
 
you need a private key for that. It is encrypting your data. The public key only decrypts...
  Permalink  

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
0 CHill60 360
1 Sarvesh Kumar Gupta 238
2 Sergey Alexandrovich Kryukov 163
3 OriginalGriff 163
4 DamithSL 153


Advertise | Privacy | Mobile
Web04 | 2.8.140709.1 | Last Updated 25 Feb 2013
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid