Problem SSL Certificate C#
Asked Answered
D

4

7

In my C# application, I got to call web services via https and validate using a .crt file that I already have. Here is the correct solution for such needs. I have updated this post once I got a working solution, thinking it might help others like me.

SOLUTION : The below code has to be executed only once in the whole application execution. With this we set the ServerCertification and SSL properties that will be used whenever a reqest will be called :

        public static void setSSLCertificate()
    {
        clientCert = new X509Certificate2(AUTHEN_CERT_FILE); // Pointing to the .crt file that will be used for server certificate verification by the client
        System.Net.ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(customXertificateValidation);
    }

    public static bool customXertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPoicyErrors)
    {
        switch (sslPoicyErrors)
        {
            case System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors:
            case System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch:
            case System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable:
                break;
        }

        return clientCert.Verify();  // Perform the Verification and sends the result
    }

A request is done normally like we do without implementing SSL. Here is a Post request code :

        private static String SendPost(String uri, String post_data)
    {
        String resData = "";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        request.KeepAlive = false;
        request.ProtocolVersion = HttpVersion.Version10;
        request.ContentType = "application/x-www-form-urlencoded";
        request.Method = "POST";

        // turn request string into byte[]
        byte[] postBytes = Encoding.ASCII.GetBytes(post_data);

        Stream requestStream = null;

        try
        {
            // Send it
            request.ContentLength = postBytes.Length;
            requestStream = request.GetRequestStream();
            requestStream.Write(postBytes, 0, postBytes.Length);
        }
        catch (WebException we)
        {   // If SSL throws exception that will be handled here
            if (we.Status == WebExceptionStatus.TrustFailure)
                throw new Exception("Exception Sending Data POST : Fail to verify server " + we.Message);
        }
        catch (Exception e)
        {
            throw new Exception("Exception Sending Data POST : " + e.Message, e.InnerException);
        }
        finally
        {
            if (requestStream != null)
                requestStream.Close();
        }

        // Get the response
        HttpWebResponse response = null;
        try
        {
            response = (HttpWebResponse)request.GetResponse();
            if (response == null)
                return "";
            StreamReader sr = new StreamReader(response.GetResponseStream());
            resData = sr.ReadToEnd().Trim();
            sr.Close();
        }
        catch (Exception e)
        {
            throw new Exception("Error receiving response from POST : " + e.Message, e.InnerException);
        }
        finally
        {
            if (response != null)
                response.Close();
        }

        return resData;
    }

Special Thanks to Dipti Mehta whose explination helped me achieve the goal to a great extend by accepting the server certificate. She helped me solve my confussions. I finally found how to verify the server certificate using .crt file by the client.

Hope this helps somebody.

Thanks

Divergence answered 19/4, 2011 at 7:35 Comment(0)
G
11

When you browse to a HTTPS site, you probably get a dialog window asking you if you want to trust the certificate provided by the webserver. So the responsibility of accepting the certificate is handled by the user. Let's get back to the webservice scenario, if you want to invoke a webservice located on a webserver which uses SSL and HTTPS there is a problem.

When you make the call from code, there is no dialog window popping up, and asking if you trust the certificate ; probably you'll get following exception:

An unhandled exception of type 'System.Net.WebException' occurred in system.dll

Additional information: The underlying connection was closed: Could not establish trust relationship with remote server.

But there is a solution for this problem, you can solve this in your code by creating your own CertificatePolicy class (which implements the ICertificatePolicy interface). In this class you will have to write your own CheckValidationResult function that has to return true or false, like you would press yes or no in the dialog window. For development purposes I've created the following class which accepts all certificates, so you won't get the nasty WebException anymore:

public class TrustAllCertificatePolicy : System.Net.ICertificatePolicy
{
  public TrustAllCertificatePolicy() 
  {}

  public bool CheckValidationResult(ServicePoint sp, X509Certificate cert,WebRequest req, int problem)
  {
    return true;
  }
}

As you can see the CheckValidationResult function always returns true, so all certificates will be trusted. If you want to make this class a little bit more secure, you can add additional checks using the X509Certificate parameter for example. To use this CertificatePolicy, you'll have to tell the ServicePointManager to use it:

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

This must be done (one time during the application life cycle) before making the call to your webservice.

Gyno answered 19/4, 2011 at 7:41 Comment(5)
You could also make use of WCF to do the validation for you, check out weblogs.asp.net/hernandl/archive/2009/04/21/…Gene
@user714721, Thanks for this perfect and straight frward explaination. Ok, so with this, if I want to use my .crt file, I got to perfomr validation inside CheckValidationResult against cert received from server. Clear till now. Then what is the use of request.ClientCertificates property. I add it or don't add it, doesn't make any differnce after adding ServicePointManager. Also, I have added this code in SendPost() that is called everytime I request for a web service. I got to remove that from SendPost() and let it only once. Can you help with ClientCertificates. ThanksDivergence
Yes, that has to be done only once. The ServicePointManager validates all kinds of certificates. But, if want to validate only certain certificates, then you can specify that too, and in that case, the request.ClientCertificates will be usefulGyno
I want to verify with a certificate (.crt) that I have. In CheckValidationResult, have used your code, & added .ClientCertificates.Add(x509cert2Obj). But it doesn't do the validation thru the passed cert. I add/remove the above line makes no difference, I can access the web service. I tried creating X509Certificate2 objet using new (string filename) and .Import(filename). But none works. it has no difference in the response. AS I understand form your above post, after adding CertificatePolicy, if I want to validae certian cert, I got to add in ClientCertificates. Where am I going wrongDivergence
Hi Dipti, can you please help me, as I have very similar problem, and I am badly in need..my proxy server application is rejecting HTTPS requests.Trochelminth
I
1

Hi Tvd,

I'm not sure that the solution you provide is actually a valid solution for the problem. Also, some of your comments regarding HttpWebRequest.ClientCertificates indicate this.

First, it is important to distinguish between the server validating a client certificate and the client validating a server certificate. Collection HttpWebRequest.ClientCertificates is used to send client certificates to the server, so the server can validate who the client is. Your question (as far as I understand it) was how server certificate which does not pass the default validation (such as a self-signed cert) can be validated against a certificate locally stored at the client.

In this case a solution is indeed to use System.Net.ServicePointManager.ServerCertificateValidationCallback and provide a custom validation. However, your validation method seems wrong: it verifies the local certificate and does not care about the cert send by the server. What I'd use is something like this:

public static bool customXertificateValidation(
    Object sender, X509Certificate certificate, 
    X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
        return true;

    return clientCert.Equals(certificate);
};

This method ensures that if the server certificate passes the default validation (no errors) it will accept it and then it compares the local, client copy of the certificate with what has been provided by the server. Only, if Equals test passes the client can safely proceed.

Indelicacy answered 5/7, 2012 at 11:46 Comment(0)
U
0

By "validate" you mean authenticate? In that case a .crt is not enough, it only contains a public key. You need the private key to authenticate yourself and put that into ClientCertificates. You can either read one from a .pfx file or import that into a certificate container and use it from there.

Unalienable answered 19/4, 2011 at 7:42 Comment(0)
F
0

Since System.Net.ICertificatePolicy is deprecated I think a correct way to do it should be create a RemoteCertificateValidationCallback delegate:

  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)
    {
        print("VALIDATE!");
        return true;
    }
Fealty answered 3/3, 2020 at 14:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.