【问题标题】:How do I set the 'ServerCertificateValidationCallback' property back to its default behavior?如何将“ServerCertificateValidationCallback”属性设置回其默认行为?
【发布时间】:2010-11-11 16:00:28
【问题描述】:

我在单个方法中使用以下代码行来显式检查和信任来自以下主机的 SSL 证书:MyTrustedCompany.com:

ServicePointManager.ServerCertificateValidationCallback = Function(obj As [Object], certificate As X509Certificate, chain As X509Chain, errors As SslPolicyErrors) (certificate.Subject.Contains("CN=MyTrustedCompany.com"))

代码没有问题 -> 100% 完美运行。

问题是,它的影响太大了。我认为它的范围只会在我贴花它的方法内,但显然它是“ServicePointManager”对象上的一个共享属性,并且必须在整个应用程序中持续存在,这是我不想要的。

问题是稍后我调用我的 Web 服务等并得到“无法建立信任关系......”异常。这是因为在上面的代码行中,我检查了特定于该方法的 SSL 证书的主机名。我快速测试了从回调中返回“真”,因此所有证书都是可信的,而不是检查特定名称(即 MyTrustedCompany)和后续请求工作。这就是我知道这个回调分配比那个单一方法到达父亲的方式。当然,我可以扩展回调以包含所有其他证书名称,但我宁愿做的是将“ServerCertificateValidationCallback”设置回其默认行为。就像下面的伪代码:

ServicePointManager.ServerCertificateValidationCallback = Nothing  'Default checking behavior

如何删除自定义验证并将其设置回默认行为?谢谢!

【问题讨论】:

标签: .net vb.net ssl ssl-certificate


【解决方案1】:

这实际上看起来有效(就这么简单)并使对象以其默认方式运行。

ServicePointManager.ServerCertificateValidationCallback = Nothing

【讨论】:

  • 你有没有想过是否有任何方法可以使这个线程安全?由于这个东西是静态的,我假设每个使用 Web 服务的线程都会在设置时调用你的委托。
  • (意识到这真的很老了......)你确定这有效吗?在 C# 中设置为 null reportedly sends everything through。
  • 是的,它确实可以将行为设置回其默认值,因为它将开始区分 SSL 证书并且不允许一切通过。它是具有return true 功能的分配回调方法,它允许一切通过。
【解决方案2】:

您只想为某些 URL 返回 true, 但除此之外,这就是你想要的,留下了使用多个代表的可能性。每个回调的逻辑可以包括通过反射进行检查,以便回调仅在从某些组件调用时返回 true,从而在某些 URL 和某些应用程序之间创建一种隧道。

使用此代码的一种方法: 1. 在对象生命周期的早期定义 mIgnoreBadCertificates 委托 2. 将包含“beSecure”代码的属性设置为 true 3. 发送 Http 请求。 4. 将属性设置为 false。这非常重要,并且应该以保证它被调用的方式实现。 IDisposable 模式是一种选择。

 private System.Net.Security.RemoteCertificateValidationCallback mIgnoreBadCertificates = new
    System.Net.Security.RemoteCertificateValidationCallback(
      delegate { return true; });

if (beSecure)
    {   //require secure communications
        System.Net.ServicePointManager.ServerCertificateValidationCallback -= mIgnoreBadCertificates;
        Iwds.EventLogger.LogVeryFrequentEvent("Requiring Good Certificates from Remote Sites");
    }
    else
    {   /// Allow connections to SSL sites that have unsafe certificates.
        System.Net.ServicePointManager.ServerCertificateValidationCallback += mIgnoreBadCertificates;
        Iwds.EventLogger.LogVeryFrequentEvent("Ignoring Bad Certificates from Remote Sites");
    }

【讨论】:

    【解决方案3】:

    解决问题的一个关键点是sender 参数到RemoteCertificateValidationCallback 是WebRequest。您可以根据您的网络请求检查发件人,因此您只需检查您的网络请求。这是我的(相对未经测试的)解决方案:

    // Main Code
    
    request = (FtpWebRequest)FtpWebRequest.Create("ftp://example.com");
    
    using(var validator = new WebRequestCertificateValidator(request))
    {
        // etc...
    }
    
    // WebRequestCertificateValidator Class
    
    public sealed class WebRequestCertificateValidator : IDisposable
    {
        private bool disposed;
    
        private WebRequest request;
    
        private RemoteCertificateValidationCallback callback;
    
        /// <summary>
        /// Creates a certificate validator that allows all certificates for the supplied web request.
        /// </summary>
        /// <param name="request">The WebRequest to validate for.</param>
        public WebRequestCertificateValidator(WebRequest request) : this(request, null)
        {
            //
        }
    
        /// <summary>
        /// Creates a certificate validator that only allows certificates for the supplied web request of the callback returns true.
        /// </summary>
        /// <param name="request">The WebRequest to validate for.</param>
        /// <param name="callback">The delegate that will be called to validate certificates for the WebRequest.</param>
        public WebRequestCertificateValidator(WebRequest request, RemoteCertificateValidationCallback callback)
        {
            this.disposed = false;
    
            this.request = request;
    
            this.callback = callback;
    
            ServicePointManager.ServerCertificateValidationCallback += this.InternalCallback;
        }
    
        private bool InternalCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            WebRequest request = sender as WebRequest;
    
            if(request != null)
            {
                if(request == this.request)
                {
                    if(this.callback != null)
                    {
                        return this.callback(sender, certificate, chain, sslPolicyErrors);
                    }
                }
            }
    
            return true;
        }
    
        public void Dispose()
        {
            if(!this.disposed)
            {
                ServicePointManager.ServerCertificateValidationCallback -= this.InternalCallback;
    
                this.callback = null;
    
                this.request = null;
    
                this.disposed = true;
            }
        }
    }
    

    【讨论】:

      【解决方案4】:
      public class OAuthRequestHandler : WebRequestHandler
      {
          public OAuthRequestHandler() : base()
          {
              base.ServerCertificateValidationCallback  += this.InternalCallback;
          }
      
          private bool InternalCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
          {
              return certificate.Subject.Contains("CN=MyTrustedCompany.com");
          }
      }
      

      在我的 program.cs 命令行测试中:

      HttpClient client = new HttpClient(new OAuthRequestHandler());
      responseString = await client.GetStringAsync("https://localhost:2345");
      

      可以使用证书和发件人参数做更多的事情,但 OP 要求上述内容。

      【讨论】:

      • 准确来说上述解决方案引入了严重的安全问题,条件应该是:return (sslPolicyErrors == SslPolicyErrors.None) && certificate.Subject.Contains("CN=MyTrustedCompany.com");
      【解决方案5】:

      确保自定义回调可靠地恢复并且整个事情也是线程安全的唯一可靠方法是将需要颠覆回调的代码放入单独的进程中。在许多情况下,这很容易做到——例如,您可以使用Process 类运行单独的进程代码并将其传递给命令行。 gRPC 或 WCF 是其他通信选项。

      这种方式只有单独的进程设置自定义回调,主进程不受影响。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-05-28
        • 1970-01-01
        • 1970-01-01
        • 2010-11-18
        • 2019-07-04
        • 2010-10-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多