161

I am trying find a way to ignore the certificate check when request a Https resource, so far, I found some helpful article in internet.

But I still have some problem. Please review my code. I just don't understand what does the code ServicePointManager.ServerCertificateValidationCallback mean.

When will this delegate method be called? And one more question, in which place should I write this code? Before ServicePointManager.ServerCertificateValidationCallback execute or before Stream stream = request.GetRequestStream()?

public HttpWebRequest GetRequest()
{
    CookieContainer cookieContainer = new CookieContainer();

    // Create a request to the server
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_remoteUrl);

    #region Set request parameters

    request.Method = _context.Request.HttpMethod;
    request.UserAgent = _context.Request.UserAgent;
    request.KeepAlive = true;
    request.CookieContainer = cookieContainer;
    request.PreAuthenticate = true;
    request.AllowAutoRedirect = false;

    #endregion

    // For POST, write the post data extracted from the incoming request
    if (request.Method == "POST")
    {
        Stream clientStream = _context.Request.InputStream;
        request.ContentType = _context.Request.ContentType;
        request.ContentLength = clientStream.Length;

        ServicePointManager.ServerCertificateValidationCallback = delegate(
            Object obj, X509Certificate certificate, X509Chain chain, 
            SslPolicyErrors errors)
            {
                return (true);
            };

            Stream stream = request.GetRequestStream();

            ....
        }

        ....

        return request;
    }
}   
Luke Girvin
  • 13,221
  • 9
  • 64
  • 84
Joe.wang
  • 11,537
  • 25
  • 103
  • 180
  • 1
    Possible duplicate of [C# Ignore certificate errors?](http://stackoverflow.com/questions/2675133/c-sharp-ignore-certificate-errors) – Matyas Feb 26 '16 at 09:20

16 Answers16

206

For anyone interested in applying this solution on a per request basis, this is an option and uses a Lambda expression. The same Lambda expression can be applied to the global filter mentioned by blak3r as well. This method appears to require .NET 4.5.

String url = "https://www.stackoverflow.com";
HttpWebRequest request = HttpWebRequest.CreateHttp(url);
request.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;

In .NET 4.0, the Lambda Expression can be applied to the global filter as such

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
Adam Venezia
  • 2,661
  • 2
  • 16
  • 10
  • 4
    Is there any per-request solution for FtpWebRequest? – Timo Jan 26 '15 at 13:11
  • It seems to exist fine in my 4.0 – Nacht Feb 27 '15 at 10:45
  • 1
    I think is by far a nicer solution to the 'global' variant, although of course I can understand why you would want it. Personally I favour a request factory which then manages this validation callback. Thanks Adam, nice solution. – The Senator May 02 '15 at 13:39
  • 3
    Returning `true` is something you can do when experimenting during development, however it is insecure. It should be a conditional. – Fred Dec 22 '15 at 15:07
  • It seems that `WebRequest.CreateHttp` has been removed after .NET 4.0. `var request = (HttpWebRequest)WebRequest.Create(url)` is the way to go. – Palec Feb 16 '17 at 15:48
  • 1
    Using `(HttpWebRequest)WebRequest.Create(url)` is perfectly valid, but on my box, `HttpWebRequest.Create(url)` still exists in a project targeting .Net 4.6.2. Chef's choice, but at this point `HttpClient` is probably the better API to use. – Adam Venezia Feb 16 '17 at 21:24
  • In my case, the server's certificate has no trusted CA signing it, and the callback never gets invoked! And I cannot add CA's in an Azure Web App. I have been stuck with this problem for 3 days! any ideas here? https://stackoverflow.com/questions/49909510/https-client-to-connect-to-server-without-trusted-chain-non-trusted-ca-on-azur – João Antunes Apr 18 '18 at 21:49
  • @JoãoAntunes I'm sorry, I don't know how to handle that case. – Adam Venezia Apr 19 '18 at 14:51
73

Since there is only one global ServicePointManager, setting ServicePointManager.ServerCertificateValidationCallback will yield the result that all subsequent requests will inherit this policy. Since it is a global "setting" it would be prefered to set it in the Application_Start method in Global.asax.

Setting the callback overrides the default behaviour and you can yourself create a custom validation routine.

Sani Huttunen
  • 23,620
  • 6
  • 72
  • 79
  • 1
    What about for a client that has no Global.asax? I am calling a REST service running on the local network from a handheld device. – B. Clay Shannon-B. Crow Raven Dec 29 '14 at 17:26
  • 3
    The question is specific to `HttpWebRequest`. If you're using any other means you'll have to look in the documentation how to accomplish this. – Sani Huttunen Dec 29 '14 at 18:29
  • I'm using WebRequest, which gets cast to HttpWebRequest, such as: ((HttpWebRequest)request).Accept = contentType; – B. Clay Shannon-B. Crow Raven Dec 29 '14 at 18:38
  • As stated in my answer: it is PREFERRED to set it in Global.asax not a requirement. You can even set it before the call to the REST service. – Sani Huttunen Dec 29 '14 at 18:40
  • Unfortunately, in my case, ServerCertificateValidationCallback is unavailable. If interested, see Updates 2 and 3 here: http://stackoverflow.com/questions/27642714/how-can-i-establish-a-secure-channel-for-ssl-tls-from-a-handheld-device – B. Clay Shannon-B. Crow Raven Dec 29 '14 at 18:41
  • 3
    See [this link](http://msdn.microsoft.com/en-us/library/bb738067.aspx#SSLwiththe.NETCompactFramework) for a possible solution. – Sani Huttunen Dec 29 '14 at 19:33
  • Using the global method, is there a way to reset it so it again validates after setting? The per request answers below will not work in the project I am working on. – user3290142 Aug 23 '19 at 19:29
  • @user3290142: `ServicePointManager.ServerCertificateValidationCallback = null;` should do the trick. Or perhaps store the old callback `var oldSCVC = ServicePointManager.ServerCertificateValidationCallback;`, change it, make your call and then restore it: `ServicePointManager.ServerCertificateValidationCallback = oldSCVC;`. Try them out. (Per request obviously uses the request object instead of the global). – Sani Huttunen Aug 23 '19 at 23:46
  • @SaniSinghHuttunen thanks. I tried what you said but did not have complete success. ServicePointManager.ServerCertificateValidationCallback is null to start with, so there is no initial callback to save off. If I make a connection ignoring SSL, the setting seems to stick regardless of setting the callback to null. If I make a second connection to a different IP and clear the callback, SSL is checked but only for that connection NOT the initial connection. – user3290142 Aug 26 '19 at 14:25
  • Hi @SaniSinghHuttunen ,I am using this delegate (ServicePointManager.ServerCertificateValidationCallback ) always returning true and suddenly start getting this exception "The request failed. The underlying connection was closed: An unexpected error occurred on a send." , unable to find what causing this. can you please guide in this case? – Sudhakar singh Jun 26 '21 at 19:07
59

This worked for me:

System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
                        System.Security.Cryptography.X509Certificates.X509Chain chain,
                        System.Net.Security.SslPolicyErrors sslPolicyErrors)
    {
        return true; // **** Always accept
    };

Snippet from here: http://www.west-wind.com/weblog/posts/2011/Feb/11/HttpWebRequest-and-Ignoring-SSL-Certificate-Errors

blak3r
  • 16,066
  • 16
  • 78
  • 98
30

Also there is the short delegate solution:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; 
Andrej Rommel
  • 308
  • 6
  • 8
  • 6
    Always returning `true` is insecure. – Fred Dec 22 '15 at 15:08
  • 13
    Yes, always trusting all SSL certificates is insecure by definition. Avoid doing so if possible. – Andrej Rommel Jan 04 '16 at 10:02
  • @AndrejRommel what is your way recommended? – Kiquenet Mar 06 '18 at 16:08
  • 2
    The recommended way is to create a valid SSL certificate and properly utilize it if you have control over the server. We ended up creating one using letsencrypt.org. – Andrej Rommel Mar 09 '18 at 09:04
  • @AndrejRommel Interesting enough I'm getting this with a HttpWebRequest and it's suddenly throwing exceptions related to this, but the cert is good, I wish more articles explained the mechanism behind what HttpWebRequest is validating that others are not, but everything just says to turn it off, which I know is wrong! – Dan Chase Oct 02 '21 at 19:15
10

Just incidentally, this is a the least verbose way of turning off all certificate validation in a given app that I know of:

ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
Irvin Dominin
  • 30,819
  • 9
  • 77
  • 111
Christian Findlay
  • 6,770
  • 5
  • 51
  • 103
9

For .net core

using (var handler = new HttpClientHandler())
{ 
    // allow the bad certificate
    handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => true;
    using (var httpClient = new HttpClient(handler))
    {
        await httpClient.PostAsync("the_url", null);
    }
}
Rich Hildebrand
  • 1,607
  • 17
  • 15
7

Rather than adding a callback to ServicePointManager which will override certificate validation globally, you can set the callback on a local instance of HttpClient. This approach should only affect calls made using that instance of HttpClient.

Here is sample code showing how ignoring certificate validation errors for specific servers might be implemented in a Web API controller.

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

public class MyController : ApiController
{

    // use this HttpClient instance when making calls that need cert errors suppressed
    private static readonly HttpClient httpClient;

    static MyController()
    {
        // create a separate handler for use in this controller
        var handler = new HttpClientHandler();

        // add a custom certificate validation callback to the handler
        handler.ServerCertificateCustomValidationCallback = ((sender, cert, chain, errors) => ValidateCert(sender, cert, chain, errors));

        // create an HttpClient that will use the handler
        httpClient = new HttpClient(handler);
    }

    protected static ValidateCert(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
    {

        // set a list of servers for which cert validation errors will be ignored
        var overrideCerts = new string[]
        {
            "myproblemserver",
            "someotherserver",
            "localhost"
        };

        // if the server is in the override list, then ignore any validation errors
        var serverName = cert.Subject.ToLower();
        if (overrideCerts.Any(overrideName => serverName.Contains(overrideName))) return true;

        // otherwise use the standard validation results
        return errors == SslPolicyErrors.None;
    }

}
Sheldon
  • 166
  • 2
  • 5
  • Won't this only work for .NET Core? (Or whenever ServerCertificateCustomValidationCallback was added to HttpClientHandler)? – phillyslick Sep 13 '19 at 18:48
  • This solution should work in .Net Framework 4.5 and later as well as .Net Core (although I have not tested it in .Net Core). – Sheldon Sep 15 '19 at 13:59
  • @Sheldon, this was a gem :) Used in in my localhost api-calls between a .net and a .net core app with success. Nice workaround without accepting all. – Large Jul 23 '20 at 22:02
5

Mention has been made that before .NET 4.5 the property on the request to access its ServicePointManager was not available.

Here is .NET 4.0 code that will give you access to the ServicePoint on a per-request basis. It doesn't give you access to the per-request callback, but it should let you find out more details about the problem. Just access the scvPoint.Certificate (or ClientCertificate if you prefer) properties.

WebRequest request = WebRequest.Create(uri);

// oddity: these two .Address values are not necessarily the same!
//  The service point appears to be related to the .Host, not the Uri itself.
//  So, check the .Host vlaues before fussing in the debugger.
//
ServicePoint svcPoint = ServicePointManager.FindServicePoint(uri);
if (null != svcPoint)
{
    if (!request.RequestUri.Host.Equals(svcPoint.Address.Host, StringComparison.OrdinalIgnoreCase))
    {
        Debug.WriteLine(".Address              == " + request.RequestUri.ToString());
        Debug.WriteLine(".ServicePoint.Address == " + svcPoint.Address.ToString());
    }
    Debug.WriteLine(".IssuerName           == " + svcPoint.Certificate.GetIssuerName());
}
Jesse Chisholm
  • 3,857
  • 1
  • 35
  • 29
  • 1
    Agreed! But then, this OP is about how to `ignore` them, not trust them. – Jesse Chisholm Mar 08 '18 at 18:40
  • Anyways, Using `ServicePoint` I _cannot_ ***always trusting*** all SSL certificates, ***neither ignore all certificates***, because has not `ServerCertificateValidationCallback` ***delegate in ServicePoint*** – Kiquenet Mar 09 '18 at 10:41
5

CA5386 : Vulnerability analysis tools will alert you to these codes.

Correct code :

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
{
   return (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != SslPolicyErrors.RemoteCertificateNotAvailable;
};
dev_O
  • 153
  • 2
  • 10
4

Based on Adam's answer and Rob's comment I used this:

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => certificate.Issuer == "CN=localhost";

which filters the "ignoring" somewhat. Other issuers can be added as required of course. This was tested in .NET 2.0 as we need to support some legacy code.

Andrej Grobler
  • 544
  • 6
  • 5
  • @karank Sorry for late reply - It can be added anywhere before the actual call, eg. before calling request.GetResponse(). Note that Issuer might contain something else in your case though. – Andrej Grobler Jun 18 '14 at 01:29
3

Unity C# Version of this solution:

void Awake()
{
    System.Net.ServicePointManager.ServerCertificateValidationCallback += ValidateCertification;
}

void OnDestroy()
{
    ServerCertificateValidationCallback = null;
}

public static bool ValidateCertification(object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
    return true;
}
2

Expressed explicitly ...

ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(CertCheck);

private static bool CertCheck(object sender, X509Certificate cert,
X509Chain chain, System.Net.Security.SslPolicyErrors error)
{
    return true;
}
dev_O
  • 153
  • 2
  • 10
1

Several answers above work. I wanted an approach that I did not have to keep making code changes and did not make my code unsecure. Hence I created a whitelist. Whitelist can be maintained in any datastore. I used config file since it is a very small list.

My code is below.

ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, error) => {
    return error == System.Net.Security.SslPolicyErrors.None || certificateWhitelist.Contains(cert.GetCertHashString());
};
Osa E
  • 1,711
  • 1
  • 15
  • 26
1

On .NetCore 3.1 you can resolve this issue by declaring a custom validation method.

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };  
  

So before making a request, declare this callback method

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };    
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("https://someurl.com/service/");  
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();  
  

This way, validation will always pass as your custom method always returns true value.

Miguel Tomás
  • 1,714
  • 1
  • 13
  • 23
0

Adding to Sani's and blak3r's answers, I've added the following to the startup code for my application, but in VB:

'** Overriding the certificate validation check.
Net.ServicePointManager.ServerCertificateValidationCallback = Function(sender, certificate, chain, sslPolicyErrors) True

Seems to do the trick.

MCattle
  • 2,897
  • 2
  • 38
  • 54
0

Tip: You can also use this method to track certificates that are due to expire soon. This can save your bacon if you discover a cert that is about to expire and can get it fixed in time. Good also for third party companies - for us this is DHL / FedEx. DHL just let a cert expire which is screwing us up 3 days before Thanksgiving. Fortunately I'm around to fix it ... this time!

    private static DateTime? _nextCertWarning;
    private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
    {
        if (error == SslPolicyErrors.None)
        {
            var cert2 = cert as X509Certificate2;
            if (cert2 != null)
            { 
                // If cert expires within 2 days send an alert every 2 hours
                if (cert2.NotAfter.AddDays(-2) < DateTime.Now)
                {
                    if (_nextCertWarning == null || _nextCertWarning < DateTime.Now)
                    {
                        _nextCertWarning = DateTime.Now.AddHours(2);

                        ProwlUtil.StepReached("CERT EXPIRING WITHIN 2 DAYS " + cert, cert.GetCertHashString());   // this is my own function
                    }
                }
            }

            return true;
        }
        else
        {
            switch (cert.GetCertHashString())
            {
                // Machine certs - SELF SIGNED
                case "066CF9CAD814DE2097D367F22D3A7E398B87C4D6":    

                    return true;

                default:
                    ProwlUtil.StepReached("UNTRUSTED CERT " + cert, cert.GetCertHashString());
                    return false;
            }
        }
    }
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • Make sure to handle the situation where your alerting mechanism can have an expired cert - or you'll end up with a stackoverflow! – Simon_Weaver Nov 21 '17 at 20:32
  • What is `ProwlUtil.StepReached` ? – Kiquenet Mar 05 '18 at 17:03
  • Sorry that’s just my own method to call the Prowl API which can send notifications to my phone. However you want to log it is good. I like to be bugged on my phone for things like this! – Simon_Weaver Mar 05 '18 at 17:04