Use client-side SSL cert with Sparkle HTTP client

Hi,


I'm looking to use the Sparkle HTTP client to connect to a non-Sparkle HTTP server which requires a client-side SSL certificate for authentication.  I've installed my client-side certificate in my certificate store, but whenever I send a request I get error 12044 which is <strong style="color: rgb69, 69, 69; font-family: 'Segoe UI', 'Lucida Grande', Verdana, Arial, Helvetica, sans-serif; line-height: 16.0048px;">ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED.  So the certificate is obviously not being presented to the server.  Does the Sparkle HTTP client have a property/method to specify a client-side SSL certificate for the request?</strong>

Thanks,
Jonathan

Hello,

Sparkle doesn't have yet such a high-level feature to set the client-side SSL certificate, unfortunately.
But you can have access to the WinHttp session handle using some hacks, and maybe with that handle you can set the client certificate yourself. Here is roughly how it might be done (untested):

TInternalClient = class(THttpCLient)
end;
TInternalEngine = class(TWinHttpEngine)
end;

Session := TWinHttpEngine(TInternalClient(SparkleHttpClient).Engine).Session;

then you might be able to call 

WinHttpSetOption(Session, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, ...)

according to this page: https://msdn.microsoft.com/en-us/library/windows/desktop/aa384076(v=vs.85).aspx

Please let me know if this helps.

Hi Wagner,


Many thanks for your help.  I think I'm almost there but the call to WinHttpSetOption is failing with error code 12018 - ERROR_WINHTTP_INCORRECT_HANDLE_TYPE.  I guess it doesn't like the handle returned by the Session property.  Here is the code :

  TInternalHTTPClient = class(THttpClient)
  end;
  TInternalHTTPEngine = class(TWinHttpEngine)
  end;

var
  httpClient: THttpClient;
  httpRequest: THttpRequest;
  httpResponse: THttpResponse;
  httpSession: HINTERNET;
  Store: HCERTSTORE;
  Cert: PCERT_CONTEXT;
begin
  httpClient := THttpClient.Create;

    // Open the 'Personal' SSL certificate store for the local machine and locate the required client-side certificate
    Cert := nil;
    Store := CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, PChar('MY'));
    if (Store <> nil) then
      Cert := CertFindCertificateInStore(Store, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, PChar('mycertsubject'), nil);

    // If a valid certificate was found then OK to create and send the HTTP request
    if (Cert <> nil) then
    begin
.....
      // Get a handle to the HTTP client session
      httpSession := TInternalHTTPEngine(TInternalHTTPClient(httpClient).Engine).Session;

      // Set the client-side SSL certificate for the request
      WinHttpCheck(WinHttpSetOption(httpSession, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, Cert, SizeOf(Cert)));

        .....
    end;

I think the WinHttpSetOption function maybe expecting the handle from the HTTP request, rather than the session handle.  That is the request created by this line in TWinHttpEngine.DoSend :

    Req := WinHttpOpenRequest(Conn, PWideChar(Request.Method),

Could that be the case?

Thanks,
Jonathan

Hi Jonathan,

please contact me directly (through our support e-mail) so I can send you some patches to make it possible for you use the request handle. 

Thanks, I've submitted the email form in the 'Direct Support' section.