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:
- 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!
Comments
- Anonymous
January 19, 2016
Somehow I was getting an ArgumentNullException and I ended up into the source code of .NET at this specific place. I didn't ask VS the load debug symbols either.. oh well, it is an empty clause catching it. reading "System.Net.ServicePointManager.SecurityProtocol" which does not exist and then get to be parsed by Enum.Parse.... Couldn't the Microsoft programmer use TryParse lol :) Anyway nice to read about this SchUseStrongCrypto setting, good to know !