WCF: SSL/TLS Failure during Add Service Reference (System.Net.Security.SslState.ProcessAuthentication)

Issue:   WCF Client application unable to consume web service metadata over SSL.

Symptoms:  Unable to use “svcutil.exe” and “Add Service Reference” feature from .net  framework and visual studio.

Point of confusion:   Is it a Visual Studio – Add service reference problem or with svcutil.exe

Reason for failure:   Client app sends TLS 1.0 as part of hand shake and server rejects it.

Verified from System.Net traces and Network traces

 

At server: If the service is available over internet, we can check the SSL requirement via –

https://sslcheck.globalsign.com/en_US 

 

 

Architecture:

Svcutil.exe internally uses the Add service reference functionality.

Stack from svcutil.exe:

 

  • So eventually it’s deep inside the System.ServiceModel Framework DLL.

  • After more digging we find that WCF DLL internally calls System.Net DLL to create the secure Channel.

system_ni!System.Net.TlsStream.ProcessAuthentication(System.Net.LazyAsyncResult)

system_ni!System.Net.Security.SslState.ProcessAuthentication(System.Net.LazyAsyncResult)

  • Which further calls 

System.Net.Security.SecureChannel..ctor()

system_ni!System.Net.Security.SslState.ValidateCreateContext()

  • Deep inside the Service Point Manager class, we do have the default value specified for the SSL protocol usage.

public class ServicePointManager

{

private static SecurityProtocolType s_SecurityProtocolType = SecurityProtocolType.Tls | SecurityProtocolType.Ssl3;

}

  • As we can see the default usage is limited to MAX TLS 1.0, in my case my application is using TLS 1.0 always and not using other protocols… for SSL handshake…

However inside the GET part, we do have extra code to use other versions…

public static SecurityProtocolType SecurityProtocol

{

 get

 {

  ServicePointManager.EnsureStrongCryptoSettingsInitialized();  <--------------------------

  return ServicePointManager.s_SecurityProtocolType;

 }

 set

 {

  ServicePointManager.EnsureStrongCryptoSettingsInitialized();

  ServicePointManager.ValidateSecurityProtocol(value);

  ServicePointManager.s_SecurityProtocolType = value;

 }

}

  • Digging more into this method reveals that there is a registry check being done, and if the value in registry is ZERO we use to lower version of TLS, otherwise high version.

private static void EnsureStrongCryptoSettingsInitialized()

{

 if (ServicePointManager.disableStrongCryptoInitialized)

 {

  return;

 }

 lock (ServicePointManager.disableStrongCryptoLock)

 {

  if (!ServicePointManager.disableStrongCryptoInitialized)

  {

   bool flag2 = true;

   int num = RegistryConfiguration.GlobalConfigReadInt("SchUseStrongCrypto", 0);

   flag2 = (num != 1);

   if (flag2)

   {

    ServicePointManager.s_SecurityProtocolType = (SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls);  <---------------------

   }

   else

   {

    ServicePointManager.s_SecurityProtocolType = (SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12);

    string value = RegistryConfiguration.AppConfigReadString("System.Net.ServicePointManager.SecurityProtocol", null);

    try

    {

     SecurityProtocolType value2 = (SecurityProtocolType)Enum.Parse(typeof(SecurityProtocolType), value);

     ServicePointManager.ValidateSecurityProtocol(value2);

     ServicePointManager.s_SecurityProtocolType = value2;

    }

    catch (OverflowException)

    {

    }

   }

   ServicePointManager.disableStrongCrypto = flag2;

   ServicePointManager.disableStrongCryptoInitialized = true;

  }

 }

}

  • So clearly we can see that “ SchUseStrongCrypto” registry key will determine the code flow.

  • What is this Registry key- SchUseStrongCrypto ?

  • After some more research looks like this is key is designed to support old protocols for interop scenarios, ideally we should always set this to 1, make sure we don’t use weak cipher and protocol when communicating over SSL channel.

  •  Key is introduced in this security magazine:

blogs.technet.com/b/srd/archive/2013/11/12/security-advisory-2868725-recommendation-to-disable-rc4.aspx

  • For some reason this key was not set on the affected box, hence when .Net app will call S Channel dll it will internally run into the if block and eventually use the TLS 1.0 as highest protocols and client were unable to get the MetaData.

Solution:

  • Add the following registry setting and restart the box.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SchUseStrongCrypto

  •  If the application is 32bit running on x64 windows, we need to modify the same key under the HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\ SchUseStrongCrypto

 

Hope this helps!