Set the SecurityProtocol (Ssl3 or TLS) on the .net HttpWebRequest per request

前端 未结 9 1177
执笔经年
执笔经年 2020-11-28 07:10

My application (.net 3.5 sp1) uses the HttpWebRequest to communicate with different endpoints, sometimes its over HTTPS where each hosting server may have a different securi

相关标签:
9条回答
  • 2020-11-28 07:40

    With Net 4.6 there is HttpClient and WinHttpHandler nuget package available for windows (from microsoft) to set SslProtocols parameters. With Net core you can use HttpClientHandler class for the same.

    using (var hc = new HttpClient(new WinHttpHandler() // should have it as a static member
    {
        AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
        SslProtocols = SslProtocols.Tls | 
                       SslProtocols.Tls11 | 
                       SslProtocols.Tls12
    }))
    {
        var r = hc.SendAsync(new HttpRequestMessage(HttpMethod.Get, "https://..."));
        r.Wait();
        Console.WriteLine(r.Result.StatusCode);
    } // using
    
    0 讨论(0)
  • 2020-11-28 07:46

    You could make a HttpWebRequest "utility class" with a static utility method for making HttpWebRequests. In the static utility method use the c# lock statement around setting the ServicePointManager.SecurityProtocol and creating a particular HttpWebRequest. The lock statement prevents other threads from the same AppDomain to execute the same code at the same time, thus the TLS protocol you just set will not get changed until the whole lock block (= critical section) is executed.

    But aware, for really high performing applications (extremely high performing!) this approcah could have a negative performance impact.

    0 讨论(0)
  • 2020-11-28 07:50

    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 >>
       }
    }
    
    0 讨论(0)
提交回复
热议问题