.NET https跨线程使用不同安全协议的请求

我维护一个非常复杂的ASP.NET应用程序(一个定制的NopCommerce 3.10)。 它需要在不同的场景下通过HTTPS连接到第三方服务器。 我是通过HttpWebRequest类来做到这一点的。

其中一些服务器配置不当:

其中一个第三方服务器(例如服务器A )需要SSL3协议类型,如果设置了其他协议类型,则只会使连接失败。 如果使用SSL3执行连接,则另一台服务器(例如服务器B )提供不正确的证书。 更确切地说,它提供了具有错误CN(通用名称)的证书。 但是,如果我从一开始就使用TLS ,证书就可以了。

我使用ServicePointManager.ServerCertificateValidationCallback回调确定了上述问题,以检查SSL策略错误。

更改安全协议是通过ServicePointManager.SecurityProtocol完成的,这是一个静态属性。 但是,客户端对我的应用程序执行的触发上述HTTPS连接的请求可能碰巧在不同的线程中并行运行。

如果我,例如:将安全协议设置为所需类型,执行HTTPS请求,然后将其设置回服务器A ,我无法保证如果同时需要连接到服务器B的请求不更改ServicePointManager.SecurityProtocol指向服务器A所需的值以外的值。 我相信这是静态变量的典型multithreading问题。

根据我的研究,我确定.NET没有为每个WebRequest实例提供使用特定SSL协议的方法。

我正在考虑以下解决方案:

  • 排队应用程序内的所有传出HTTPS连接,以确保每个连接都有正确的SSL协议
  • 为每个HTTPS请求构建一个单独的应用程序域(由https://stackoverflow.com/a/3107692/1288522建议)
  • 将HTTPS请求更改为低级TCP连接,并为每个TCP连接强制执行不同的SSL协议
  • 制作一个代理asp.net应用程序,它将对传出请求进行排队

注意:排队不会造成巨大的性能损失,因为所有客户端请求的一小部分实际上都会到达相关代码。

然而,考虑到应用程序架构或粗略的解决方法(第三种解决方案),上述解决方案需要进行困难的重构

我的问题与msdn上的这个非常类似,但是没有得到满意的答案。

是否有更直接或有效的方法来确保每个https请求使用特定的SSL协议?

我们遇到了同样的问题,并采用了您提到的app-domain方法,根据此处提出的方法实现了一个解决方案,这是一个非常好的关于如何在单独的应用程序域中管理代码执行的文章:

http://www.superstarcoders.com/blogs/posts/executing-code-in-a-separate-application-domain-using-c-sharp.aspx

我们正在使用他的Isolated类:

  public sealed class Isolated : IDisposable where T : MarshalByRefObject { private AppDomain _domain; private readonly T _value; public Isolated() { _domain = AppDomain.CreateDomain("Isolated:" + Guid.NewGuid(), null, AppDomain.CurrentDomain.SetupInformation); var type = typeof(T); _value = (T)_domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName); } public T Value { get { return _value; } } public void Dispose() { if (_domain == null) return; AppDomain.Unload(_domain); _domain = null; } } 

然后我们有一个标准WebClient的包装器,它允许设置协议:

 public class WebClient : MarshalByRefObject, IWebClient { public WebClientResponse GetResponse(string address) { return GetResponse(address, null); } public WebClientResponse GetResponse(string address, string securityProtocol) { if (!string.IsNullOrWhiteSpace(securityProtocol)) ServicePointManager.SecurityProtocol = (SecurityProtocolType)Enum.Parse(typeof(SecurityProtocolType), securityProtocol); var response = new WebClientResponse(); try { using (var wc = new System.Net.WebClient()) { //  } } catch (Exception ex) { response.Exception = new GetResponseException(string.Format("Unable to get response from {0}", address), ex); } return response; } } [Serializable] public class WebClientResponse { public Exception Exception { get; set; } public string Response { get; set; } } [Serializable] public class GetResponseException : Exception { public GetResponseException(string message, Exception innerException) : base(message, innerException) { } public GetResponseException(SerializationInfo info, StreamingContext context) : base(info, context) { } } 

将它们绑在一起,我们有代码确定它是否需要覆盖当前设置的协议。 如果是这样,它会旋转隔离的应用程序域,如果不是,它会使用现有的WebClient:

 ... WebClientResponse webClientResponse; if (!string.IsNullOrWhiteSpace(ForceSecurityProtocol)) { using (var isolated = new Isolated()) { webClientResponse = isolated.Value.GetResponse(url, ForceSecurityProtocol); } } else { webClientResponse = _webClient.GetResponse(url); } ... 

请注意,我们的使用不在我们应用程序的极高吞吐量区域,因此无论使用此方法支付的性能价格是否真的没有影响。 如果我们要将这样的东西放在通过我们的网络应用程序阻碍大量流量的地方,我们就会做一些测试。

您可以简单地使用您提到的回调( ServicePointManager.ServerCertificateValidationCallback )来实现自定义validation逻辑:只有当错误来自该恶意服务器时才会绕过错误。