Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / security / cryptography

SSL Peer Verification

4.85/5 (4 votes)
21 Apr 2011CPOL4 min read 50.8K  
Verifying the Server Certificate on the client side using a CA file

Introduction

When calling a Web Service using HTTPS and SSL, Host Verification and Peer Verification are to be handled for security. This article shows and explains how Peer Verification can be done successfully when calling a Web Service via HTTPS that is secured by SSL.

Using the Code

Mostly Web Services are hosted on HTTPS and are secured by SSL so others can't use it. Many times when we surf an HTTPS site, we get a certificate dialog asking us to trust the provider. This is the Server Certificate that is sent by the web server. We accept the certificate and proceed ahead. In desktop applications, when we perform a similar task like accessing a Web Service which uses HTTPS and SSL, we can't receive any dialog to trust the certificate and there comes the problem.

This can be easily achieved by creating our own CertificatePolicy class that implements the ICertificatePolicy interface. For instance, such a class could be written as:

C#
public class MyPolicy : ICertificatePolicy {

    public MyPolicy() {
    }

    public bool CheckValidationResult(ServicePoint srvPoint, 
                X509Certificate certificate, 
                WebRequest request, int certificateProblem)
    {
        // Perform operations to verify
        // the certificate and return true or false 
        return true;
    }
}

And can set:

C#
ServerCertificateValidationonCallback = MyPolicy();

The above code can also be written using a delegate of System.Net.ServicePointManager.ServerCertificateValidationonCallback.

C#
System.Net.
ServicePointManager.ServerCertificateValidationCallback = {
     (Object sender, X509Certificate certificate, X509Chain chain, 
             SslPolicyErrors sslPolicyErrors) {
         return true;
     }
};

The above code accepts the Server Certificate (the dialog that pops up on the browser to trust the provider). It implicitly returns true, that means it accepts any server certificate and proceeds ahead accessing the Web Service.

This is so simple and easy. But if SSL is specially meant for security and cryptography and can be bypassed so easily, then what sense does SSL make? Nothing, right?

When our client verifies this server certificate against any certificate file, i.e., without verifying the verify certificate to verify the server certificate, the client can't access any Web Service from the provider. This is like providing a password or authenticating password. Without actually authenticating, a Web Service that gives and takes data should be easy to fake. Thus we need to verify the Server Certificate using a "Right" version of the verification certificate*. Verification Certificate is just a CA file that compares the server certificates data. If both match, it returns true and lets us access Web Services, else it returns false and doesn't let us access a Web Service and its confidential data.

Note *: The certificate used to verify is not the same as Client Certificate, both are different and so are their roles. A Client Certificate is passed to the server if the server needs a Client Certificate. It can be set using the request.ClientCertificates property.

Now comes the point: how do we verify that the server certificate is correct or the server certificate matches the verify certificate? Fake users can change the Server Certificate or the certificate used to verify, so we got to take care on both sides.

Here is the code that helps you to accept the server certificate and also verify the certificate against a CA file.

Remember: Acceptance of Server Certificate code should be set only once in the whole application life cycle. And it will be called automatically whenever a request is made. So for ease of coding, we will separate the SSL Certificate part with the request part.

Here is the code to set and handle the SSL Certificate part:

C#
// Certificate to verify 
private X509Certificate2 verifyCert = null;   

private void setSSLCertificate() {
     veriftCert = new X509Certificate2("ca_verify.crt");
     ServicePointManager.ServerCertificateValidationCallback += new 
       System.Net.Security.RemoteCertificateValidationCallback(
       customCertificateValidation);
}

public bool customCertificateValidation(Object sender, 
       X509Certificate certificate, X509Chain chain, 
       SslPolicyErrors sslPolicyErrors) 
{
      switch(sslPolicyErrors) {
           case RemoteCertificateChainErrors:
           case RemoteCertificateNameMismatch:
           case RemoteCertificateNotAvailable:
                   break;
      }
      // With this we have accepted the server certificate
      // Now, VERIFY the certificate against verifyCert
      // If details of both the certificates matches, then returns true, else false 
      return verifyCert.Verify();   
}

That's it. This is how Peer Verification is handled and should be taken care of without any hard coding or comparing in code.

Now whenever any request is performed, first customCertificateValidation() will be called and if that returns true, then the request will be able to proceed, else a WebException will be caught.

C#
private void SendPost(String post_data) {

     HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
       <a href="https://ae-q01.com/">https://ae-q01.com/</a>);
     request.KeepAlive = true;
     request.ContentType = "application/x-www-form-urlencoded";
     request.Method = "POST";
     byte[] postBytes = Encoding.ASCII.GetBytes(psot_data);
     Stream requestStream = null;

     try {
           // Send
           request.ContentLength = postBytes.Length;
           requestStream = request.GetRequestStream();
           requestStream.Write(postBytes, 0, postBytes.Length);
     } catch (WebException we) {

     // If our customCertificateValidation returns false, here it will be caught. 
     // You can check the status of WebException we.Status,
     // mostly it returns TrustFailure and handle them
     } catch(Exception e) {
          // Handle other normal request errors
     } finally {
          if (requestStream != null)
              requestStream.Close();  
     }
}

Don't forget to call setSSLCertificate() at the start of the application or whenever appropriate before calling any Web Service.

That's all for SSL Peer Verification. I am sure this will definitely help more developers who actually cross-check SSL verification and not just accept the server certificate blindly.

Let me know how it works.

Points of Interest

I wanted to perform SSL Peer Verification. I Googled and asked questions in forums a lot, but could only find how to accept a Server Certificate, not how to verify it, which is most important when you are accessing a Web Service that implements SSL security. Finally, when I figured out the solution, I thought about sharing it with all to make others' work a bit easier.

History

This is my first article on CodeProject. If I have made any mistakes, kindly let me know of it; any tips for improvements are appreciated.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)