title: SSL with WinHTTP
body: <p>I have created a simple web server with winhttp that has only one client, a website. It worked perfectly until I enabled SSL. I don't get any errors, and everything seems to work, but the website gets error code 104 when it tries to connect, and I don't see any activity in the server. The website was created by professionals, so the bug is very likely to be in my code. Since I know nothing about web programming, I created a test client in c++, to debug the problem.
  Without SSL it can connect to the server. But when I try to set the clients certificate, it fails. So now I have two problems.</p>
  
  <p>Here is the relevant part of the server code :</p>
  
  <pre><code>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;
  
  SOCKADDR_IN sa;
  HTTP_SERVICE_CONFIG_SSL_SET scssl;
  memset(&amp;sa,0,sizeof(sa));
  sa.sin_addr.S_un.S_addr = 0;
  sa.sin_family = AF_INET;
  sa.sin_port = 443;
  scssl.KeyDesc.pIpPort = (SOCKADDR*)&amp;sa;
  scssl.ParamDesc.AppId = AppID;
  scssl.ParamDesc.DefaultCertCheckMode = 0;
  scssl.ParamDesc.DefaultFlags = HTTP_SERVICE_CONFIG_SSL_FLAG_NEGOTIATE_CLIENT_CERT;
  scssl.ParamDesc.DefaultRevocationFreshnessTime = 0;
  scssl.ParamDesc.DefaultRevocationUrlRetrievalTimeout = 10000;
  scssl.ParamDesc.pSslCertStoreName = L"MY";
  scssl.ParamDesc.pDefaultSslCtlIdentifier = NULL;
  scssl.ParamDesc.pDefaultSslCtlStoreName = NULL;
  scssl.ParamDesc.pSslHash = (void*)ServerCertHash;
  scssl.ParamDesc.SslHashLength = ARRAYSIZE(ServerCertHash);
  ret = HttpSetServiceConfiguration(NULL,HttpServiceConfigSSLCertInfo,&amp;scssl,sizeof(scssl),NULL);
  if(ret!=NO_ERROR)
      return;
  
  ret = HttpCreateHttpHandle(&amp;m_RequestQueue,0);
  if(ret!=NO_ERROR)
      return;
  
  wcsncpy(m_Url,L"https://+:443/WebShop/",256);
  ret = HttpAddUrl(m_RequestQueue,m_Url,NULL);
  if(ret!=NO_ERROR)
      return;
  </code></pre>
  
  <p>The documentation says nothing about what happens, when pDefaultSslCtlIdentifier is NULL, but I guess it means the server accepts any trusted certificate. I tried to set the one I need, but than the HttpSetServiceConfiguration call failed.</p>
  
  <p>On the client side WinHttpSetOption fails, and GetLastError() returns 6. Sometimes I see access violation errors in the debug output. The reported addresses are close to the value of request.
  The client code :</p>
  
  <pre><code>char* types[] = {"application/soap+xml",NULL};
  hint = InternetOpen("WebTestClient",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
  if(hint!=NULL)
  {
      InternetSetStatusCallback(hint,&amp;HTTPStatusCallbackFunc);
      hsession = InternetConnect(hint,"127.0.0.1",443,NULL,NULL,3,0,NULL);
      if(hsession!=NULL)
      {
          hreqest = HttpOpenRequest(hsession,"GET","/WebShop/","HTTP/1.1",NULL,(LPCSTR*)types,INTERNET_FLAG_SECURE|INTERNET_FLAG_IGNORE_CERT_CN_INVALID,NULL);
          if(hreqest!=NULL)
          {
              if(SetupSSL(hreqest))
              {
                  if(HttpSendRequest(hreqest,NULL,0,inbuf,inlen))
                  {
                      ...
                  }
              }
              InternetCloseHandle(hreqest);
          }
          InternetCloseHandle(hsession);
      }
      InternetCloseHandle(hint);
  }
  
  bool SetupSSL(HINTERNET request)
  {
      HCERTSTORE store = CertOpenSystemStore(NULL,"MY");
      DWORD ret = 0;
      bool ok = false;
  
      if(store==NULL)
          return false;
      PCCERT_CONTEXT context = CertFindCertificateInStore(store,X509_ASN_ENCODING,0,CERT_FIND_SUBJECT_STR,L"WebClient",NULL);
      if(context!=NULL)
      {
          // this fails
          ok = WinHttpSetOption(request,WINHTTP_OPTION_CLIENT_CERT_CONTEXT,(LPVOID)context,sizeof(CERT_CONTEXT))==TRUE;
          if(!ok)
              ret = GetLastError();  // returns 6
          CertFreeCertificateContext(context);
      }
      CertCloseStore(store,0);
      return ok;
  };
  </code></pre>
  
  <p>I created the certificates with this script:</p>
  
  <pre><code>makecert -r -pe -n "CN=BeeLSoft" -ss CA -sr LocalMachine -a sha1 -sky signature -cy authority -sv Root.pvk Root.cer
  
  makecert -pe -n "CN=beelsoft.dyndns.org" -a sha1 -sky exchange -eku 1.3.6.1.5.5.7.3.1 -ic Root.cer -iv Root.pvk -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -sv Server.pvk Server.cer
  pvk2pfx -pvk Server.pvk -spc Server.cer -pfx Server.pfx
  
  makecert -pe -n "CN=WebClient" -a sha1 -sky exchange -eku 1.3.6.1.5.5.7.3.1 -ic Root.cer -iv Root.pvk -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -sv Client.pvk Client.cer
  pvk2pfx -pvk Client.pvk -spc Client.cer -pfx Client.pfx
  </code></pre>
  
  <p>I don't understand any of that stuff, but its supposed to create certificates suitable for SSL.</p>
  
  <p>If there is a good tutorial on this subject, that would be nice. What I found so far was just enough to write the code above.</p>