Allowing Untrusted SSL Certificates with HttpClient
Asked Answered
A

13

205

I'm struggling to get my Windows 8 application to communicate with my test web API over SSL.

It seems that HttpClient/HttpClientHandler does not provide and option to ignore untrusted certificates like WebRequest enables you to (albeit in a "hacky" way with ServerCertificateValidationCallback).

Any help would be much appreciated!

Aurie answered 23/9, 2012 at 14:45 Comment(1)
In case you're using .NET Core you might be interested in this answer.Dorty
H
13

With Windows 8.1, you can now trust invalid SSL certs. You have to either use the Windows.Web.HttpClient or if you want to use the System.Net.Http.HttpClient, you can use the message handler adapter I wrote: http://www.nuget.org/packages/WinRtHttpClientHandler

Docs are on the GitHub: https://github.com/onovotny/WinRtHttpClientHandler

Hahn answered 23/9, 2013 at 2:15 Comment(4)
Is there a way to do this without using all of your code? In other words, what is the gist of your solution?Annabell
The gist of the solution is to wrap the windows http client handler and use that as the implementation of the HttpClient. It's all in this file: github.com/onovotny/WinRtHttpClientHandler/blob/master/… feel free to copy it in but not sure why not just use the package.Hahn
@OrenNovotny so your solution is not bound to the Windows version?Snowber
I implemented this in my UWP app, and I used the filters example below. I still get the same error.Iceni
H
191

A quick and dirty solution is to use the ServicePointManager.ServerCertificateValidationCallback delegate. This allows you to provide your own certificate validation. The validation is applied globally across the whole App Domain.

ServicePointManager.ServerCertificateValidationCallback +=
    (sender, cert, chain, sslPolicyErrors) => true;

I use this mainly for unit testing in situations where I want to run against an endpoint that I am hosting in process and am trying to hit it with a WCF client or the HttpClient.

For production code you may want more fine grained control and would be better off using the WebRequestHandler and its ServerCertificateValidationCallback delegate property (See dtb's answer below). Or ctacke answer using the HttpClientHandler. I am preferring either of these two now even with my integration tests over how I used to do it unless I cannot find any other hook.

Herstein answered 14/8, 2013 at 12:39 Comment(11)
Not the downvoter, but one of the biggest problems with the ServerCertificateValidationCallback is that it's basically global to your AppDomain. So if you're writing a library that needs to make calls to a site with an untrusted certificate and employ this workaround, you are changing the behavior of the entire application, not just your library. Also, people should always be careful with the 'blindly return true' approach. It has serious security implications. It should read /* inspect the supplied parameters and then carefully decide whether to */ return true;Sheepfold
@Sheepfold Certainly a valid point and worth mentioning but it is still a valid solution. Perhaps after re reading the original question by the OP it appears he was already aware of the ServerCertificateValidationCallback handlerHerstein
I would recommend to at least filter on some criteria like senderTeacher
I really have to recommend the WebRequestHandler option vs. the global ServicePointManager.Guggenheim
It better to use WebRequestHandler instead of ServicePointManager that sets this rule globally for whole apps. So I have down voted it, just to make better answer go higher.Climb
You don't have to use ServicePointManager globally, you can use ServicePointManager.GetServicePoint(Uri) (see docs) to get the service point which applies only to calls to that URI. You can then set properties and handle events based on that subset.Feedback
What is the alternative to make this work with a SOAP service?Achieve
I have already implemented this in my app, but it I still get the error anyway.Iceni
The RemoteCertificateValidationCallback handler never gets hit, I'm on .net 4.6.1 if that makes any difference and am using System.Net.HttpClientChildbirth
@Childbirth - Can you post your code as another question. It might be useful to others as well.Herstein
@bronumski, never mind, the cert was invalid, it used a combination of md5 cert hash and TSL1.2, this means it was failing before the handler was hit. On .net core 2 this worked, and chrome and IE will work with this cert but technically its not valid to use md5 so .net full framework is not accepting it and you can't override it, solution was to fix the cert.Childbirth
C
190

If you're attempting to do this in a .NET Standard library, here's a simple solution, with all of the risks of just returning true in your handler. I leave safety up to you.

var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback = 
    (httpRequestMessage, cert, cetChain, policyErrors) =>
{
    return true;
};

var client = new HttpClient(handler);
Cumulostratus answered 8/10, 2017 at 1:59 Comment(5)
this results in platform not suppoerted when referenced from uwpRiposte
worked for me in UWP. perhaps the support coverage has matured in 14 months. note: this hack should be necessary in test/dev env only (where self-signed certs are being used)Langue
I ran this code and always meet the System.NotImplementedException. I don't know why.Barbra
You don't HAVE to return true here - you can actually do your own validation! In my case I have a global app and some clients don't have up-to-date root certs so they can't validate HTTPS properly - I just check the cert thumbprint in these cases.Blinking
I don't think setting the ClientCertificateOptions property is necessary. That property relates to a client side certificate being used so that the server can identify the client. From the docs "Gets or sets a value that indicates if the certificate is automatically picked from the certificate store or if the caller is allowed to pass in a specific client certificate." (learn.microsoft.com/en-us/dotnet/api/…)Metasomatism
P
103

Have a look at the WebRequestHandler Class and its ServerCertificateValidationCallback Property:

using (var handler = new WebRequestHandler())
{
    handler.ServerCertificateValidationCallback = ...

    using (var client = new HttpClient(handler))
    {
        ...
    }
}
Paez answered 23/9, 2012 at 16:4 Comment(7)
Thanks for the response however I've already looked into that - it's not present in .NET for Windows 8 Store apps.Aurie
However, creating one's own HttpClientHandler or HttpMessageHandler derived class is easy enough, especially given the fact that the WebRequestHandler source is readily available.Guggenheim
@ThomasS.Trias any sample about it ? derived class ?Revisionist
Using HttpClientHandler ?Revisionist
@Revisionist this reply is from 2012, it is 6 years old already, and yes - many people these times were sure that this is right way to use httpclient :)Aprilaprile
don't use the "using" pattern: learn.microsoft.com/en-us/azure/architecture/antipatterns/…Counterweight
"Provides desktop-specific features not available to Windows Store apps or other environments."Veliavelick
F
79

If you are using System.Net.Http.HttpClient I believe correct pattern is

var handler = new HttpClientHandler() 
{ 
    ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};

var http = new HttpClient(handler);
var res = http.GetAsync(url);

Link to official doc

Funerary answered 29/10, 2019 at 10:41 Comment(2)
Thank you Jakub, I had developed a program to connect to a bank ... but they still sent me invalid certificates (!!!). You saved me from certain insanity ... I have included a 'thanks to Jakub Sturc' in my codeMat
That will be fun for Jakub when there's a MitM attack and funds are drainedTetchy
C
32

Most answers here suggest to use the typical pattern:

using (var httpClient = new HttpClient())
{
 // do something
}

because of the IDisposable interface. Please don't!

Microsoft tells you why:

And here you can find a detailed analysis whats going on behind the scenes: You're using HttpClient wrong and it is destabilizing your software

Official Microsoft link: HttpClient

HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors.

Regarding your SSL question and based on Improper Instantiation antipattern # How to fix the problem

Here is your pattern:

class HttpInterface
{
 // https://learn.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem
 // https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient#remarks
 private static readonly HttpClient client;

 // static initialize
 static HttpInterface()
 {
  // choose one of these depending on your framework
  
  // HttpClientHandler is an HttpMessageHandler with a common set of properties
  var handler = new HttpClientHandler()
  {
      ServerCertificateCustomValidationCallback = delegate { return true; },
  };
  // derives from HttpClientHandler but adds properties that generally only are available on full .NET
  var handler = new WebRequestHandler()
  {
      ServerCertificateValidationCallback = delegate { return true; },
      ServerCertificateCustomValidationCallback = delegate { return true; },
  };

  client = new HttpClient(handler);
 }
 
 .....
 
 // in your code use the static client to do your stuff
 var jsonEncoded = new StringContent(someJsonString, Encoding.UTF8, "application/json");

 // here in sync
 using (HttpResponseMessage resultMsg = client.PostAsync(someRequestUrl, jsonEncoded).Result)
 {
  using (HttpContent respContent = resultMsg.Content)
  {
   return respContent.ReadAsStringAsync().Result;
  }
 }
}
Counterweight answered 15/2, 2019 at 10:20 Comment(2)
The question is for self signed cert's trust relationship. Your whole answer is (even thought its valid) about making the HttpClient thread safe.Visionary
It's not only about "thread safety". I wanted to show the "right" way of using of the httpclient with "disabled" SSL verification. Other answers "globally" modify the certificate manager.Counterweight
L
26

Or you can use for the HttpClient in the Windows.Web.Http namespace:

var filter = new HttpBaseProtocolFilter();
#if DEBUG
    filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired);
    filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
    filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName);
#endif
using (var httpClient = new HttpClient(filter)) {
    ...
}
Leucopoiesis answered 27/2, 2015 at 10:39 Comment(3)
Can I use System.Net.Http, System.Web and Windows.Web.Http together ?Revisionist
HttpClient doesn't seem to have this override in .NET Standard or UWP.Iceni
don't use the "using" pattern: learn.microsoft.com/en-us/azure/architecture/antipatterns/…Counterweight
D
19

Use this in Startup.cs for ASP.NET Core project:

public void ConfigureServices(IServiceCollection services)
{
    // other code
    
    services
        .AddHttpClient<IMyService, MyService>(client =>
        {
            client.BaseAddress = new Uri(myConfiguration.BaseUrl);
        })
        .ConfigurePrimaryHttpMessageHandler(() =>
        {
            // Allowing Untrusted SSL Certificates
            var handler = new HttpClientHandler();
            handler.ClientCertificateOptions = ClientCertificateOption.Manual;
            handler.ServerCertificateCustomValidationCallback =
                (httpRequestMessage, cert, cetChain, policyErrors) => true;

            return handler;
        });
}
Dichromic answered 5/5, 2022 at 11:5 Comment(2)
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewApparel
This is good. I needed this .NET Core version, on how to append it to the AddHttpClient call. I think it's very useful to have answers that cover all the moving target frameworks related to the OP question, even if not the exact version requested.Farrah
H
13

With Windows 8.1, you can now trust invalid SSL certs. You have to either use the Windows.Web.HttpClient or if you want to use the System.Net.Http.HttpClient, you can use the message handler adapter I wrote: http://www.nuget.org/packages/WinRtHttpClientHandler

Docs are on the GitHub: https://github.com/onovotny/WinRtHttpClientHandler

Hahn answered 23/9, 2013 at 2:15 Comment(4)
Is there a way to do this without using all of your code? In other words, what is the gist of your solution?Annabell
The gist of the solution is to wrap the windows http client handler and use that as the implementation of the HttpClient. It's all in this file: github.com/onovotny/WinRtHttpClientHandler/blob/master/… feel free to copy it in but not sure why not just use the package.Hahn
@OrenNovotny so your solution is not bound to the Windows version?Snowber
I implemented this in my UWP app, and I used the filters example below. I still get the same error.Iceni
A
10

I found an example in this Kubernetes client where they were using X509VerificationFlags.AllowUnknownCertificateAuthority to trust self-signed root certificates. I slightly reworked their example to work with our own PEM encoded root certificates. Hopefully this helps someone.

namespace Utils
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Net.Security;
  using System.Security.Cryptography.X509Certificates;

  /// <summary>
  /// Verifies that specific self signed root certificates are trusted.
  /// </summary>
  public class HttpClientHandler : System.Net.Http.HttpClientHandler
  {
    /// <summary>
    /// Initializes a new instance of the <see cref="HttpClientHandler"/> class.
    /// </summary>
    /// <param name="pemRootCerts">The PEM encoded root certificates to trust.</param>
    public HttpClientHandler(IEnumerable<string> pemRootCerts)
    {
      foreach (var pemRootCert in pemRootCerts)
      {
        var text = pemRootCert.Trim();
        text = text.Replace("-----BEGIN CERTIFICATE-----", string.Empty);
        text = text.Replace("-----END CERTIFICATE-----", string.Empty);
        this.rootCerts.Add(new X509Certificate2(Convert.FromBase64String(text)));
      }

      this.ServerCertificateCustomValidationCallback = this.VerifyServerCertificate;
    }

    private bool VerifyServerCertificate(
      object sender,
      X509Certificate certificate,
      X509Chain chain,
      SslPolicyErrors sslPolicyErrors)
    {
      // If the certificate is a valid, signed certificate, return true.
      if (sslPolicyErrors == SslPolicyErrors.None)
      {
        return true;
      }

      // If there are errors in the certificate chain, look at each error to determine the cause.
      if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0)
      {
        chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;

        // add all your extra certificate chain
        foreach (var rootCert in this.rootCerts)
        {
          chain.ChainPolicy.ExtraStore.Add(rootCert);
        }

        chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
        var isValid = chain.Build((X509Certificate2)certificate);

        var rootCertActual = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
        var rootCertExpected = this.rootCerts[this.rootCerts.Count - 1];
        isValid = isValid && rootCertActual.RawData.SequenceEqual(rootCertExpected.RawData);

        return isValid;
      }

      // In all other cases, return false.
      return false;
    }

    private readonly IList<X509Certificate2> rootCerts = new List<X509Certificate2>();
  }
}
Apostasy answered 9/5, 2019 at 10:48 Comment(1)
Works perfectly in case of communicating from AWS Lambda to onpromise http service (with self signed certificate)Lipread
H
7

If this is for a Windows Runtime application, then you have to add the self-signed certificate to the project and reference it in the appxmanifest.

The docs are here: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx

Same thing if it's from a CA that's not trusted (like a private CA that the machine itself doesn't trust) -- you need to get the CA's public cert, add it as content to the app then add it to the manifest.

Once that's done, the app will see it as a correctly signed cert.

Hahn answered 4/1, 2013 at 23:36 Comment(0)
C
2

I don't have an answer, but I do have an alternative.

If you use Fiddler2 to monitor traffic AND enable HTTPS Decryption, your development environment will not complain. This will not work on WinRT devices, such as Microsoft Surface, because you cannot install standard apps on them. But your development Win8 computer will be fine.

To enable HTTPS encryption in Fiddler2, go to Tools > Fiddler Options > HTTPS (Tab) > Check "Decrypt HTTPS Traffic".

I'm going to keep my eye on this thread hoping for someone to have an elegant solution.

Carty answered 4/2, 2013 at 23:38 Comment(0)
C
1

I found an example online which seems to work well:

First you create a new ICertificatePolicy

using System.Security.Cryptography.X509Certificates;
using System.Net;

public class MyPolicy : ICertificatePolicy
{
  public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, 
int certificateProblem)
  {
    //Return True to force the certificate to be accepted.
    return true;
  }
}

Then just use this prior to sending your http request like so:

System.Net.ServicePointManager.CertificatePolicy = new MyPolicy();

http://www.terminally-incoherent.com/blog/2008/05/05/send-a-https-post-request-with-c/

Crashing answered 24/2, 2017 at 22:13 Comment(1)
ServicePointManager.CertificatePolicy is deprecated: learn.microsoft.com/en-us/dotnet/framework/whats-new/…Fuze
L
0

For Xamarin Android this was the only solution that worked for me: another stack overflow post

If you are using AndroidClientHandler, you need to supply a SSLSocketFactory and a custom implementation of HostnameVerifier with all checks disabled. To do this, you’ll need to subclass AndroidClientHandler and override the appropriate methods.

internal class BypassHostnameVerifier : Java.Lang.Object, IHostnameVerifier
{
    public bool Verify(string hostname, ISSLSession session)
    {
        return true;
    }
}
 
internal class InsecureAndroidClientHandler : AndroidClientHandler
{
    protected override SSLSocketFactory ConfigureCustomSSLSocketFactory(HttpsURLConnection connection)
    {
        return SSLCertificateSocketFactory.GetInsecure(1000, null);
    }
 
    protected override IHostnameVerifier GetSSLHostnameVerifier(HttpsURLConnection connection)
    {
        return new BypassHostnameVerifier();
    }
}

And then

var httpClient = new System.Net.Http.HttpClient(new InsecureAndroidClientHandler());
Lousewort answered 14/12, 2020 at 6:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.