Inspect server certificate using HttpClient
Asked Answered
G

3

10

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.

Gorgeous answered 14/7, 2017 at 6:12 Comment(0)
G
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();
    }
  }
}
Gorgeous answered 14/7, 2017 at 16:8 Comment(2)
This doesn't seem to work for me. The Certificate is always null.Talanian
Note that this won't work on .NET Core: github.com/dotnet/runtime/issues/29301Spindlelegs
E
4

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

Everara answered 14/7, 2017 at 6:35 Comment(2)
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.Gorgeous
System.Net.Http.WebRequestHandler doesn't seem to be available in .NET Core, even in .NET 5.0.Spindlelegs
A
3

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

Authorize answered 14/7, 2017 at 6:19 Comment(1)
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.Gorgeous

© 2022 - 2024 — McMap. All rights reserved.