Monday, April 25, 2011

Storing and Utilizing X509 Certificates in Microsoft Dynamics CRM 2011 Online or On-Premise

I have been working on a project recently where I have been tasked with making secure web service calls using X509 client certificates. This proved to be a semi-daunting task but we soon got it figured out as a team.

Here is how it works:

1. You must first store the X509 certificates as a web resource in CRM. I made a post on storing files of any type as web resources here. http://mileyja.blogspot.com/2011/04/storing-any-file-type-in-microsoft.html

2. Now you must retrieve the content attribute of the web resource and use it to instantiate an X509 certificate. This example retrieves the needed client certificate, and attaches it to the secure call to a web service that requires client-certificate authentication.

  RetrieveMultipleRequest rmr = new RetrieveMultipleRequest();
                        RetrieveMultipleResponse resp = new RetrieveMultipleResponse();
                        Entity wr = new Entity();

                        QueryExpression query = new QueryExpression()
                        {
                            EntityName = "webresource",
                            ColumnSet = new ColumnSet("content"),
                            Criteria = new FilterExpression
                            {
                                FilterOperator = LogicalOperator.And,
                                Conditions = 
                                {
                                    new ConditionExpression
                                    {
                                        AttributeName = "name",
                                        Operator = ConditionOperator.Equal,
                                        Values = { "new_mywebresourcename" }
                                    }
                                }
                            }
                        };

                        rmr.Query = query;
                        resp = (RetrieveMultipleResponse)service.Execute(rmr);
                        wr = (Entity)resp.EntityCollection.Entities[0];
                        byte[] certbytes = Convert.FromBase64String(wr.Attributes["content"].ToString());

                        X509Certificate2 cert = new X509Certificate2(certbytes, "certpassword");

                        //make call
                        using (WebServiceClient client = new WebServiceClient())
                        {
                            

                            client.Url = @"https://domain.com/mysecurewebservice.asmx";
                            client.ClientCertificates.Add(cert);
                            entity.Attributes.Add("description", client.HelloWorld());

                        }

3. Now let's examine the gotcha that was involved in this scenario.

- You have to use the X509Certificate2 class, not the X509Certificate class when you instantiate the certificate from the returned byte array if you intend to use the call in a sandbox environment.

 

 

9 comments:

  1. Hi Jamie,

    Thanks for this post, really helpful in terms of accessing a certificate from Dynamics, but alas there is a further complication I am trying to solve. I am having to use a .pfx file rather then .cer in order to get access to a private key. Your method above works magic at getting the web resource down and successfully creating the certificate object, however the certificate object fails to load the private key.

    The exception:
    'System.Security.SecurityException' System.Security.Cryptography.AsymmetricAlgorithm {System.Security.SecurityException}

    All other property values are loaded successfully.

    Thanks in advance,
    Dillon

    ReplyDelete
    Replies
    1. Yikes, I haven't tried that one before. Also, if this is CRM Online you could be running into some other Sandboxing issue.

      Delete
    2. Hi Dillon, Jamie,

      Did you find any worked around to get it work with .pfx file?

      Could you please help me on this?

      Thanks in advance!!
      Regards,
      Suraj

      Delete
  2. Hi Jamie,
    Even I got stuck at the same point. I was trying to use your technique for oAuth authentication in Plugin. Certificate is created from the web resource without any issues but the moment I say signingCertificate.PrivateKey it throws the security exception.

    It will be of great help if you can guide me here.

    Rgds,
    Nik

    ReplyDelete
    Replies
    1. Interesting. The only scenario I have tried before (successfully) was using a specified cert to connect to an authenticated webservice. Something could have changed with UR-12 potentially. In my situation I could not use the certificate with X509Certificate but had to use X509Certificate2. I am wondering if something changed in an Update rollup as far as what's allowed in sandboxing with regards to certificates or whether your scenario is just different than mine.

      Delete
    2. I am going to guess it's an update as I only have started to hear about these issues in the recent past and this post has been up for a long time. I might have revisit this one in the not too distant future. I recently realized that they also changed the format of SOAP responses in UR12 so that the actual response XML is in a whole new property now.

      It's kind of frustrating.

      Delete
  3. I think it is more of Sandboxing issue because in the sample above you have added the client certificate to call an authenticated service but here we are trying to retrieve the privateKey from certificate for signing as well. Do you think this is an issue or I am totally off target.

    ReplyDelete
  4. Hi Any updates on the same. Did you manage to find any answer?

    ReplyDelete
    Replies
    1. That is a little beyond my example. In my example I don't think the private key is contained in the certificate, I could be wrong. I was just more concerned about storing and retrieving the file for use.

      Delete