Dynamics 365, TLS 1.2 and How to Fix Your Apps

Leave a comment

TLS, “Transport Layer Security” is a protocol that provides privacy between 2 applications. TLS has two layers, 1) TLS Record Protocol, which provides security, and 2) TLS Handshake Protocol, which allows the apps to authenticate. TLS 1.2 is the latest release. As the specification states:

The primary goal of the TLS protocol is to provide privacy and data
   integrity between two communicating applications.

TLS is supported by the Microsoft .NET Framework in the following ways:

  • .NET 4.0 supports TLS 1.0
  • .NET 4.5+ supports TLS1.2 through a code update shown below
  • .NET 4.6+ supports TLS 1.2 by default

It is important to be on higher versions of TLS as older versions are subject to exploits. If your .NET code is running older versions of TLS, and the apps you are integrating with use newer protocols, you will run into errors.

In this post we will simulate getting an error trying to use a TLS version lower than TLS 1.2, running against Dynamics 365 version 9.0 and the ways to fix it. You can read more about the Dynamics 365 update to TLS 1.2 on the Microsoft website here.

Simulating the Problem

First, create a new Console app:

Change the .NET version to 4.5.2:

Now add code. We will do something simple, like connecting to Dynamics 365 and getting the version:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Xrm.Tooling.Connector;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Sdk.Messages;

namespace Carl.D365TLS12
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var connectionString = @"AuthType = Office365; Url=https://yourorg.crm.dynamics.com/;Username=yourusername;Password=yourpassword";
                CrmServiceClient conn = new CrmServiceClient(connectionString);

                IOrganizationService service;
                service = (IOrganizationService)conn.OrganizationWebProxyClient != null ? (IOrganizationService)conn.OrganizationWebProxyClient : (IOrganizationService)conn.OrganizationServiceProxy;

                RetrieveVersionRequest versionRequest = new RetrieveVersionRequest();
                RetrieveVersionResponse versionResponse = (RetrieveVersionResponse)service.Execute(versionRequest);

                Console.WriteLine("Microsoft Dynamics CRM version {0}.", versionResponse.Version);
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("An error has occurred: " + ex.ToString());
                Console.ReadLine();
            }
        }
    }
}

Run the app. You will see the error “Object not set to an instance of an object” and “Unable to Login to Dynamics CRM”:

If we look at the connection object, we see “Unable to Login to Dynamics CRM”:

Resolving the Problem Through Code

Add the line at the beginning:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

So the code looks like:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Xrm.Tooling.Connector;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Sdk.Messages;
using System.Net;

namespace Carl.D365TLS12
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

                var connectionString = @"AuthType = Office365; Url=https://yourorg.crm.dynamics.com/;Username=yourusername;Password=yourpassword";
                CrmServiceClient conn = new CrmServiceClient(connectionString);

                IOrganizationService service;
                service = (IOrganizationService)conn.OrganizationWebProxyClient != null ? (IOrganizationService)conn.OrganizationWebProxyClient : (IOrganizationService)conn.OrganizationServiceProxy;

                RetrieveVersionRequest versionRequest = new RetrieveVersionRequest();
                RetrieveVersionResponse versionResponse = (RetrieveVersionResponse)service.Execute(versionRequest);

                Console.WriteLine("Microsoft Dynamics CRM version {0}.", versionResponse.Version);
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("An error has occurred: " + ex.ToString());
                Console.ReadLine();
            }
        }
    }
}

When we run this, we get the correct connection and output:

Resolving the Problem Through Registry Entries

Setting your machine to use strong cryptography can be done with the following PowerShell command.

64-Bit:

Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord

32-Bit:

Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord

After doing this, your code will work as before without any updates.

Resolving by Updating the .NET Version

If you change your app to use .NET 4.6 and above, TLS 1.2 will be used by default and your code will work as expected.

ABOUT CARL DE SOUZA

Carl de Souza is a developer and architect focusing on Microsoft Dynamics 365, BI, Web, Cloud and Data Science.

carldesouza.comLinkedIn Twitter | YouTube

 

Leave a Reply

Your email address will not be published. Required fields are marked *