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

Dan*_*scu 18 c# asp.net ssl multithreading

我维护了一个非常复杂的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所需的值以外的值.我相信这是静态变量的典型多线程问题.

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

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

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

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

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

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

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

Vea*_*tch 16

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

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

我们正在使用他的Isolated类:

  public sealed class Isolated<T> : 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我们有一个标准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())
            {
              // <do stuff>
            }
        }
        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)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

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

...
        WebClientResponse webClientResponse;

        if (!string.IsNullOrWhiteSpace(ForceSecurityProtocol))
        {
            using (var isolated = new Isolated<WebClient>())
            {
                webClientResponse = isolated.Value.GetResponse(url, ForceSecurityProtocol);
            }
        }
        else
        {
            webClientResponse = _webClient.GetResponse(url);
        }
...
Run Code Online (Sandbox Code Playgroud)

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