Accepting a Flaky Certificate When Doing an SSL POST

by bob on January 16, 2007

I needed to communicate with a web server that requires an SSL connection yet does not have a valid SSL cert. This is actually not that uncommon when dealing with various business partners. In this case it’s a large multinational corporation with a web farm. The certificate is issued to www.bigcorp.com but what actually answers is something like www86.bigcorp.com. Even giant, soulless corporations don’t want to buy an SSL certificate for every box in their web farm!

My client’s legacy ASP application used an ActiveX component and to accept such certificates you simply set a property to true — something like AcceptAllSSLCerts. It was that simple! In porting this to .NET, though, it’s one of those things that gets more involved, because this is an area where the .NET class libraries are a bit on the thin, low-level side. There no doubt are third party products that simplify this, but following is a piece of code that did the trick for me:

First, create the following class:

internal class AcceptAllCertificatePolicy : System.Net.ICertificatePolicy {

  public AcceptAllCertificatePolicy() {}

  public bool CheckValidationResult(System.Net.ServicePoint sp,
                                    System.Security.Cryptography.X509Certificates.X509Certificate cert,
                                    System.Net.WebRequest req,
                                    int certProb) {
    return true;
  }

}

Now, execute the following call just before doing the POST:

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

This works fine in both .NET 1.1 and 2.0. In 2.0 you’ll get a warning that this call is deprecated and replaced with some kid of callback; in the rush of deadlines I have not bothered to track that down. Another thing I did not need to check was whether setting the ServicePointManager.CertificatePolicy property is global for your entire app (I’m pretty sure it is) and whether it needs to be called before every POST (I’m pretty sure not). However in my case it doesn’t matter because it’s a non performance-critical routine that usually is only called once per program run anyway. Of course if I had other SSL connections to make where I didn’t want to relax this requirement I’d need to figure out how to toggle this on and off.

This actually makes a nice real-world example of how to make decisions about allocating your precious time. Many of us forget that the customer is paying us to solve a problem, not to solve it elegantly. There is a line you don’t want to cross, of course, where you write slovenly code that will come back to haunt you someday. But in this case, if I’d spent an extra 20 minutes making sure I was using the latest API call and tweaking for efficiency that would be unlikely to ever be noticed, then I’m not serving the customer’s best interests. What serves their interests is to make sure it works right.

I will confess that this has gone on my List of Interesting Things To Check Out Someday in my Spare Time (Ha-ha). No problem fine-tuning your code on your own time … it’s just not always warranted on the customer’s time.

Update:  I ran across the .NET 2.0 solution to this issue here:

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;
        };

{ 3 comments… read them below or add one }

Mrinal May 24, 2007 at 1:59 pm

This does not work on Vista. Would you know the solution that works on Vista?

Bob responds: Not offhand, but may I ask, what exactly are the symptoms of it not working on Vista? And can you verify that your implementation of this works correctly on, say, XP?

Mrinal May 24, 2007 at 3:07 pm

I use the code just before calling my web service. It is a c# .Net 2.0 client code that connects to server view web service. When I use a SSL enabled connection from windows XP, the connection goes through. The same code when run on windows Vista throws an exception of type System.Net.WebException. I verified that the WSDL for the web service is accessible from the browser on Vista.

Bob responds: Sounds like you’re doing the right things but are running afoul of Vista’s tighter security defaults. I would research Vista system security policies and suspect you’ll find that by default it’s either impossible to accept an expired cert and/or a cert where the URI does not exactly match the cert; or you can’t do it without elevation. Hopefully this can be configured away at your discretion. I know for my client’s project that prompted this posting, they cannot dictate to their partner for a server-side resolution. There are times when you implicitly trust the URI and the cert just enables SSL encryption, and isn’t really intended to be used for verification of identity.

Bob July 19, 2007 at 7:19 am

I cant get the ServerCertificateValidationCallback to work. I keep getting “unable to connect to remote host”
The web service has an invalid SSL cert, but I dont care I still want to call the methods on the web service. Here is my code:

ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;

hew.apshealthcare.healtheweb_test.HealthEWebService webservice = new hew.apshealthcare.healtheweb_test.HealthEWebService();

DataSet ds = webservice.GetEligibleFamilyMembers(this.tbMemberId.Text);
this.GridView1.DataSource = ds;
this.GridView1.DataBind();
}

public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return true;

}

Leave a Comment

Previous post:

Next post: