Click here to Skip to main content
15,892,927 members
Please Sign up or sign in to vote.
3.67/5 (3 votes)
See more:
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:


C++
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:


C++
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

1 solution

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...
 
Share this answer
 

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



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900