Dynamics 365, TLS 1.2 and How to Fix Your Apps

4 Comments

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.

THANKS FOR READING. BEFORE YOU LEAVE, I NEED YOUR HELP.
 

I AM SPENDING MORE TIME THESE DAYS CREATING YOUTUBE VIDEOS TO HELP PEOPLE LEARN THE MICROSOFT POWER PLATFORM.

IF YOU WOULD LIKE TO SEE HOW I BUILD APPS, OR FIND SOMETHING USEFUL READING MY BLOG, I WOULD REALLY APPRECIATE YOU SUBSCRIBING TO MY YOUTUBE CHANNEL.

THANK YOU, AND LET'S KEEP LEARNING TOGETHER.

CARL

https://www.youtube.com/carldesouza

 

ABOUT CARL DE SOUZA

Carl de Souza is a developer and architect focusing on Microsoft Dynamics 365, Power BI, Azure, and AI.

carldesouza.comLinkedIn Twitter | YouTube

 

4 Responses to Dynamics 365, TLS 1.2 and How to Fix Your Apps

  1. Hardcoded “ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;” is not the best approach, because we already have TLS 1.3 supported by 4.8 framework.
    App should support the latest security protocol without hardcoded values.
    Something like this
    System.Net.ServicePointManager.SecurityProtocol=Tls | Tls11 | Tls12 | Tls13

  2. Adding the following line directly above the ServiceClient constructor worked!:

    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

    I can’t believe it was that simple after pulling my hair out for days. Thank you for this post. Just a note, I am on .NET 4.71, but ServiceClient was still using TLS 1 (confirmed w/Wireshark) and failing until I added the line above.

Leave a Reply

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