Data Encryption

At times you might want to store sensitive information such as social security numbers or passwords in your Microsoft Dynamics CRM system. Information like this usually should not be stored as plain text in the database because anyone with administrator access to the database could view the information, and if a malicious user compromises your database, vital customer information is easy to steal. Out of the box, Microsoft Dynamics CRM does not provide a way for you to encrypt data stored in an attribute. However, you can use some custom coding techniques to implement data encryption. We will consider two different data encryption scenarios:

  • One-way encryption

  • Two-way encryption

One-Way Encryption

Let’s assume we added a new custom attribute to the Contact entity to store passwords for a portal site. For this example, assume the portal site is using SSL to communicate with the server so that passwords aren’t sent to the server in the clear. Because you’ll be storing the passwords of external contacts, let’s assume that your security requirements dictate that you cannot store passwords as plain text in the Microsoft Dynamics CRM database. By default, Microsoft Dynamics CRM stores attribute data in SQL Server as plain text. Fortunately, we can use an encryption algorithm and custom code to encrypt the contact’s passwords in the database .

Note

Note

For the following example, two new custom attributes, named new_username and new_ password, have been added to the Contact entity.

For this example, the portal users create their user accounts from an external Web site, and the application stores this data in the Microsoft Dynamics CRM database. Example 4-6 shows the code to create the UI for our Create User Account page.

Example 4-6. CreateUserAccount.aspx

<%@ Page Language="C#"
    AutoEventWireup="true"
    CodeFile="CreateUserAccount.aspx.cs"
    Inherits="Security_CreateUserAccount" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        User Name: <asp:TextBox ID="username" runat="server" />
        <br />
        Password: <asp:TextBox ID="password" TextMode="Password" runat="server" />
        <br />
        First Name <asp:TextBox ID="firstname" runat="server" />
        <br />
        Last Name <asp:TextBox ID="lastname" runat="server" />
        <br />
        <input type="submit" value="Create Account" />
    </div>
    </form>
</body>
</html>

The CreateUserAccount.aspx page is pretty straightforward. We add a few text boxes to capture the information we need to create the new Contact record and a submit button. Example 4-7 has the full source code for the code behind the page.

Example 4-7. CreateUserAccount.aspx.cs

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk;
using System.Security.Cryptography;
using System.Text;

public partial class Security_CreateUserAccount : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Page.IsPostBack)
        {
            string userName = this.username.Text;
            string password = this.password.Text;
            string firstName = this.firstname.Text;
            string lastName = this.lastname.Text;

            CrmService service =
                CrmServiceUtility.GetCrmService(ConfigurationManager.AppSettings
                   ["CrmServer"], ConfigurationManager.AppSettings["OrgName"]);
        DynamicEntity contact = new DynamicEntity();
        contact.Name = EntityName.contact.ToString();

        StringProperty userNameProp = new StringProperty();
        userNameProp.Name = "new_username";
        userNameProp.Value = userName;

        contact.Properties.Add(userNameProp);

        // encrypt the entered password

        MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();

        byte[] encryptedPassword;

        UTF8Encoding textencoder = new UTF8Encoding();

        encryptedPassword = md5.ComputeHash(textencoder.GetBytes(password));

        md5.Clear();

        StringProperty passwordProp = new StringProperty();
        passwordProp.Name = "new_password";
        passwordProp.Value = Convert.ToBase64String(encryptedPassword);

        contact.Properties.Add(passwordProp);

        StringProperty firstNameProp = new StringProperty();
        firstNameProp.Name = "firstname";
        firstNameProp.Value = firstName;

        contact.Properties.Add(firstNameProp);

        StringProperty lastNameProp = new StringProperty();
        lastNameProp.Name = "lastname";
        lastNameProp.Value = lastName;

        contact.Properties.Add(lastNameProp);

        service.Create(contact);
    }
  }
}

Note

Note

This example uses the Microsoft.Crm.Sdk.dll and Microsoft.Crm.Sdk.TypeProxy.dll assemblies and not the direct Web references to the Microsoft Dynamics CRM SDK. Therefore, we need to use a DynamicEntity class to create the Contact record because of the two custom attributes we are populating.

The creation of the actual Contact in Microsoft Dynamics CRM is straightforward as well. We simply grab the values the user entered on our form, create an instance of the DynamicEntity class, populate its properties, and then call the Create method on the CrmService. For this example we use a fast and easy to implement encryption algorithm named Message-Digest Algorithm 5 (MD5). Because MD5 offers one-way encryption, after we encrypt and store the password, we will not be able to return it to its original plain text characters. MD5 always encrypts a given string to the same value. In the case of passwords this is not a problem because we can just encrypt the user’s password at logon time and compare it to the encrypted value stored in the database.

The classes we need to implement MD5 encryption are located in the System.Security. Cryptography namespace. We first use the UTF8Encoding class to get a byte array from our password string. The ComputeHash method on the MD5CryptoServiceProvider instance is then called to encrypt our byte array. Microsoft Dynamics CRM does not provide us with an attribute type capable of storing binary data, so we need to convert our encrypted bytes into a string that can be stored in a nvarchar database field. To do this we use the Convert. ToBase64String method:

passwordProp.Value = Convert.ToBase64String(encryptedPassword);

Now that we are storing the encrypted passwords, let’s briefly discuss how we can use them in our site’s login page. When a user enters her credentials, we first have to run the entered password text through the same MD5 encryption. This encrypted value can then be used to query for a matching contact record. If a match is found, you can now direct the user to the home page of your portal.

Two-Way Encryption

One-way data encryption works well for information like passwords, but what happens when you need to retrieve the encrypted data and revert it back to its original plain text? In cases like these, you need to use two-way encryption. In this section we will create a plug-in to encrypt social security numbers being stored in the Contact entity. Example 4-8 has the full source code for this plug-in.

Note

Note

For the following example, a new attribute named new_ssn has been added to the Contact entity.

Example 4-8. SSNEncryptor.cs source code

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Crm.Sdk;
using ProgrammingWithDynamicsCrm4.Plugins.Attributes;
using System.Security.Cryptography;

namespace ProgrammingWithDynamicsCrm4.Plugins
{
    [PluginStep("Create",
                PluginStepStage.PreEvent,
                PrimaryEntityName = "contact",
                FilteringAttributes = "new_ssn")]

    [PluginStep("Update",
                PluginStepStage.PreEvent,
                PrimaryEntityName = "contact",
                FilteringAttributes = "new_ssn")]

    public class SSNEncryptor : IPlugin
    {
        public void Execute(IPluginExecutionContext context)
        {
            DynamicEntity target = (DynamicEntity)context.InputParameters[ParameterName.
                Target];

            if (target.Properties.Contains("new_ssn"))
            {
                string ssn = (string)target["new_ssn"];
 
                byte[] aInput = ASCIIEncoding.ASCII.GetBytes(ssn);

                TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider =
                    new TripleDESCryptoServiceProvider();
                MD5CryptoServiceProvider MD5CryptoServiceProvider =
                    new MD5CryptoServiceProvider();

                tripleDESCryptoServiceProvider.Key =
                    MD5CryptoServiceProvider.ComputeHash(ASCIIEncoding.ASCII.
                        GetBytes("secretkey"));
                tripleDESCryptoServiceProvider.Mode = CipherMode.ECB;
                ICryptoTransform iCryptoTransform = tripleDESCryptoServiceProvider.
                    CreateEncryptor();

                target.Properties["new_ssn"] =
                Convert.ToBase64String(iCryptoTransform.TransformFinalBlock(aInput, 0,
                    aInput.Length));
            }
        }
    }
}

This plug-in runs whenever a Contact record is created or updated. If the new_ssn attribute is populated or changed, it takes the new value and encrypts it before saving the information in the database. Notice the string "secretkey" being passed into the MD5CryptoServiceProvider class’s ComputeHash method. This same string is used later to decrypt the data. In a real-world application, you would want to use a larger random string of characters for your secret key.

Next we need a way for end users to update social security numbers. If we stopped here, end users would see a mess of characters displayed in our Contact form’s social security number field (Figure 4-3).

Encrypted Social Security Number display

Figure 4-3. Encrypted Social Security Number display

Therefore, we need to decrypt our data prior to it being displayed on the form. One solution is to create a plug-in that fires on the Retrieve message for a Contact. The plug-in verifies that the new_ssn attribute is in the returned property collection, decrypts the data, and then updates the new_ssn property with the plain text. Example 4-9 shows the full source code for this plug-in.

Example 4-9. SSNDecryptor.cs source code

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Crm.Sdk;
using ProgrammingWithDynamicsCrm4.Plugins.Attributes;
using System.Security.Cryptography;

namespace ProgrammingWithDynamicsCrm4.Plugins
{
    [PluginStep("Retrieve",
                PluginStepStage.PostEvent,
                PrimaryEntityName = "contact")]
    [PluginStep("RetrieveMultiple",
                PluginStepStage.PostEvent,
                PrimaryEntityName = "contact")]
    public class SSNDecryptor : IPlugin
    {

        public void Execute(IPluginExecutionContext context)
        {
            if (context.MessageName == "Retrieve")
            {
                DynamicEntity contact =
                                 (DynamicEntity)context.OutputParameters[ParameterName
                                     .BusinessEntity];
                DecryptSSN(contact);
            }
            else
            {
                BusinessEntityCollection results =
            (BusinessEntityCollection)context.OutputParameters[ParameterName.
                BusinessEntityCollection];

                foreach (DynamicEntity result in results.BusinessEntities)
                {
                    DecryptSSN(result);
                }
             }
         }

         private static void DecryptSSN(DynamicEntity contact)
         {
             if (contact.Properties.Contains("new_ssn"))
             {
                 string ssn = (string)contact.Properties["new_ssn"];
                 byte[] aInput = Convert.FromBase64String(ssn);

                 TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider =
                     new TripleDESCryptoServiceProvider();
                 MD5CryptoServiceProvider MD5CryptoServiceProvider =
                     new MD5CryptoServiceProvider();
                     tripleDESCryptoServiceProvider.Key =
                         MD5CryptoServiceProvider.ComputeHash(ASCIIEncoding.ASCII.
                             GetBytes("secretkey"));
                     tripleDESCryptoServiceProvider.Mode = CipherMode.ECB;
                     ICryptoTransform iCryptoTransform = tripleDESCryptoServiceProvider.
                         CreateEncryptor();

                     target.Properties["new_ssn"] =
                     Convert.ToBase64String(iCryptoTransform.TransformFinalBlock(aInput, 0,
                         aInput.Length));
                   }
               }
           }
        }

After you deploy this plug-in, end users never even know that Microsoft Dynamics CRM encrypts the confidential information because users will see the plain text social security numbers on the Contact form. However, if you query for the new_ssn field in the Microsoft Dynamics CRM database, you get the encrypted value.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset