I know this question is old, but the issue remains even with .Net 4.7.2. In my case, I have a multi-threaded application that is talking to two endpoints. One endpoint only works with TLS 1.2, and the other endpoint only works with TLS 1.0 (the team responsible for that one is working on fixing their endpoint so it will also support TLS 1.2).
To work around this, I moved the service calls for the the endpoint that only works with TLS 1.0 to a separate class in the same assembly, and then loaded the assembly into a separate AppDomain. By doing this, I can use this global variable:
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
just for the calls to the broken endpoint, while also having calls to the TLS 1.2 endpoint (which don't require setting ServicePointManager.SecurityProtocol to anything specific) continue to work. This also ensures that when the good endpoint is upgraded to TLS 1.3 I don't need to re-release my application. My application is multi-threaded and high capacity, so locking or try/finally are not adequate solutions.
Here is the code I used to load the assembly into a separate domain. Note that I load the assembly from it's current location (Aspnet Tempfiles) so that it doesn't lock the assembly in the bin directory and block future deployments.
Also, note that the proxy class inherits MarshalByRefObject so that it is used as a transparent proxy which preserves System.Net.ServicePointManager with it's own value in it's own AppDomain.
This seems like a silly limitation on the part of the .Net framework, I wish we could just specify the Protocol directly on the web request instead of jumping through hoops, especially after years of this. :(
This code does work, hope it helps you out! :)
private static AppDomain _proxyDomain = null;
private static Object _syncObject = new Object();
public void MakeACallToTls10Endpoint(string tls10Endpoint, string jsonRequest)
{
if (_proxyDomain == null)
{
lock(_syncObject) // Only allow one thread to spin up the app domain.
{
if (_proxyDomain == null)
{
_proxyDomain = AppDomain.CreateDomain("CommunicationProxyDomain");
}
}
}
Type communicationProxyType = typeof(CommunicationProxy);
string assemblyPath = communicationProxyType.Assembly.Location;
// Always loading from the current assembly, sometimes this moves around in ASPNet Tempfiles causing type not found errors if you make it static.
ObjectHandle objectHandle = _proxyDomain.CreateInstanceFrom(assemblyPath, communicationProxyType.FullName.Split(',')[0]);
CommunicationProxy communicationProxy = (CommunicationProxy)objectHandle.Unwrap();
return communicationProxy.ExecuteHttpPost(tls10Endpoint, jsonRequest);
}
Then, in a separate class:
[Serializable]
public class CommunicationProxy : MarshalByRefObject
{
public string ExecuteHttpPost(string tls10Endpoint, string jsonRequest)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
// << Bunch of code to do the request >>
}
}