8

I'm rewriting some web handling code in WinForms and switching from HttpWebRequest to HttpClient. There's one last thing I require that I cannot seem to find out how to accomplish.

In HttpWebRequest, I can capture the certificate from the web server I'm connecting to and display it:

...
HttpWebRequest request = CreateHttpRequest(destUri);

HttpWebResponse response = (HttpWebResponse)request.GetResponse();

cert = request.ServicePoint.Certificate;

if (cert != null) 
{ 
  cert2 = new X509Certificate2(cert); 
  X509Certificate2UI.DisplayCertificate(cert2);
}
...

I cannot find the equivalent way capture the certificate using HttpClient:

//... Use HttpClient.
using (HttpClient client = new HttpClient())
{
  using (HttpResponseMessage response = await client.GetAsync(destUri))
  {
    using (HttpContent content = response.Content)
    {
      string result = await content.ReadAsStringAsync();
    }
  }
}

How/where can I do this here? I don't know how to get to the ServicePoint.Certificate.

LordChariot
  • 543
  • 1
  • 6
  • 10

3 Answers3

8

Apparently, you don't need to get the certificate from the ServicePointManager.ServerCertificateValidationCallback. You can find it from ServicepointManager itself, like so:

//... Use HttpClient.
using (HttpClient client = new HttpClient())
{
  using (HttpResponseMessage response = await client.GetAsync(destUri))
  {
    // Get Certificate Here
    var cert = ServicePointManager.FindServicePoint(destUri).Certificate;
    //
    using (HttpContent content = response.Content)
    {
      string result = await content.ReadAsStringAsync();
    }
  }
}
LordChariot
  • 543
  • 1
  • 6
  • 10
3

Bulding on Remus answer - here is something I've cobbled up in LinqPad, which does give you access to your certificate:

var handler = new WebRequestHandler();
handler.UseDefaultCredentials = true;
handler.AllowPipelining = true;
handler.ServerCertificateValidationCallback =  (sender, cert, chain, error) => {
    //do something with cert here
    cert.Subject.Dump();
    //useless validation on my part
    return true;
};


using (HttpClient client = new HttpClient(handler))
{
  using (HttpResponseMessage response = await client.GetAsync("https://google.com"))
  {
    using (HttpContent content = response.Content)
    {
        //foo
    }
  }
}

Dump() the outputs the following:

CN=*.google.com, O=Google Inc, L=Mountain View, S=California, C=US

CN=www.google.de, O=Google Inc, L=Mountain View, S=California, C=US

CN=www.google.com, O=Google Inc, L=Mountain View, S=California, C=US

Community
  • 1
  • 1
Marco
  • 22,856
  • 9
  • 75
  • 124
  • That's getting closer. I don't understand how I can get return the certificate itself from the callback so I can store it and use it. I used to be able to get it with cert=request.ServicePoint.Certificate; but doing that in the callback is eluding me. – LordChariot Jul 14 '17 at 14:39
  • System.Net.Http.WebRequestHandler doesn't seem to be available in .NET Core, even in .NET 5.0. – EM0 Jan 19 '21 at 16:14
2

Use a WebRequestHandler with a proper certificate validation callback. see HttpClient, HttpClientHandler, and WebRequestHandler Explained for example.

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • Thanks. I actually had an HttpClientHandler that I didn't show and didn't realize there was a WebRequestHandler extension. When I tried to use that, there wasn't a reference. Apparently you must manually add the System.Net.Http.WebRequest reference and not just the using statement. – LordChariot Jul 14 '17 at 14:34