【问题标题】:WCF per connection server certificate validationWCF 每个连接服务器证书验证
【发布时间】:2013-12-24 06:26:44
【问题描述】:

我试图绕过 https 证书验证仅对我们自己的测试环境(多台机器),同时尝试为所有其他连接保留证书验证。

从在线阅读来看,大多数(如果不是全部)与 WCF 相关的建议似乎都指向以下类似的内容

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

但是,这是一个全局设置,我想仅将其应用于特定连接。这甚至可能/受支持的使用场景吗?

【问题讨论】:

    标签: c# wcf ssl


    【解决方案1】:

    在使用 .net 4.5 时,我终于找到了一个真正的解决方案。

    此代码允许您仅为特定 WCF 客户端使用自定义验证器。

    它已针对带有BasicHttpSecurityMode.Transport 的 BasicHttpBinding 进行了测试。

    ClientBase.ClientCredentials.ServiceCertificate 中有一个名为 SslCertificateAuthentication 的新属性。

    您可以使用X509ServiceCertificateAuthentication 初始化此属性,您可以在其中提供自定义X509CertificateValidator

    例如:

    // initialize the ssl certificate authentication
    client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication()
    {
       CertificateValidationMode = X509CertificateValidationMode.Custom,
       CustomCertificateValidator = new CustomValidator(serverCert)
    };
    
    // simple custom validator, only valid against a specific thumbprint
    class CustomValidator : X509CertificateValidator
    {
        private readonly X509Certificate2 knownCertificate;
    
        public CustomValidator(X509Certificate2 knownCertificate)
        {
            this.knownCertificate = knownCertificate;
        }
    
        public override void Validate(X509Certificate2 certificate)
        {
            if (this.knownCertificate.Thumbprint != certificate.Thumbprint)
            {
                throw new SecurityTokenValidationException("Unknown certificate");
            }
        }
    }
    

    【讨论】:

    • 这启发了我下面的答案
    【解决方案2】:

    似乎在 .NET 4.5 中您可以执行以下操作:

    var request = (HttpWebRequest)WebRequest.Create(url);
    request.ServerCertificateValidationCallback += 
        (sender, certificate, chain, sslPolicyErrors) => true
    

    我最初并没有意识到这一点,因为您实际上必须将Create 方法的结果转换为HttpWebRequest,因为抽象WebRequest 不包含此委托。

    【讨论】:

      【解决方案3】:

      类似这样的:

      ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(ValidateCert);
      
      public static bool ValidateCert(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
      {
          string requestHost;
      
          if(sender is string)
          {
              requestHost = sender.ToString();
          }
          else
          {
              HttpWebRequest request = sender as HttpWebRequest;
      
              if(request != null)
              {
                  requestHost = request.Host;
              }
          }
      
          if(!string.IsNullOrEmpty(requestHost) && requestHost == "my_test_machine")
              return true;
      
          return sslPolicyErrors == SslPolicyErrors.None;
      }
      

      注意sender参数上的documentation

      根据 CertificatePolicy 属性,传递给 RemoteCertificateValidationCallback 的发送方参数可以是主机字符串名称或派生自 WebRequest(例如 HttpWebRequest)的对象

      免责声明 - 我没有对此进行测试,我是根据文档编写的。 YMMV。

      【讨论】:

      • 我希望有一个可以设置的每个连接设置。我想避免在全局验证器上检查主机。似乎非常无效。但是,这样做可以,如果在赏金结束时间附近没有更好的答案,我会奖励积分。
      • 我不知道每个连接的设置,但也许比我更开明的人会加入。如果只是在测试时你可以将代码包装在#if DEBUG...
      • 由于没有明显的方法可以为每个 WCF 连接执行此操作,因此这是最接近它的方法。太糟糕了,您不能简单地将证书验证应用于单个连接,这真的很有帮助。
      • 但是,这不适用于 WSDL 服务。有什么想法吗?
      【解决方案4】:

      您实际上可以使用以下代码禁用每个客户端/通道的 SSL 证书验证:

      var noCertValidationAuth = new X509ServiceCertificateAuthentication() 
      { 
          CertificateValidationMode = X509CertificateValidationMode.None 
      };
      client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = noCertValidationAuth;
      

      它实际上是这个帖子中answer 的一个变体,所以在此表示感谢。

      【讨论】:

        【解决方案5】:

        我想向您推荐一种更“洁净”的方式来做到这一点(从信息安全的角度来看):

        1. Create 为您的开发人员机器提供的自签名唯一证书
        2. Add此证书到您的测试服务实例上的“受信任的根”证书存储
        3. 将证书添加到应用程序的允许列表(如果存在)

        这将使您无需在代码中创建“后门”即可使用您的服务。这也将允许您仅在 Test 和 Prod 实例上具有相同代码时限制对测试环境的测试访问。

        【讨论】:

        • 感谢您的建议,我们知道这种方法。虽然这可行,但这将要求我们在需要测试连接的每台机器上重新部署证书,所以这并不是我们真正想要的。
        【解决方案6】:

        您可以做的是在您的 web.config 文件中放置一个设置。

        然后在您的代码中检查 web.config 文件中的值。然后根据该值设置证书验证。

        编辑

        一个选项是:

        String url = "https://www.stackoverflow.com";
        HttpWebRequest request = HttpWebRequest.CreateHttp(url);
        request.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => { return true; };
        

        另一个是:http://weblog.west-wind.com/posts/2011/Feb/11/HttpWebRequest-and-Ignoring-SSL-Certificate-Errors

        编辑 2

        试试这样的:

        ServicePointManager.ServerCertificateValidationCallback += customXertificateValidation;
        
        
        private static bool customXertificateValidation(object sender, X509Certificate cert, 
        X509Chain chain, SslPolicyErrors error)
        {
            if (check something here)
           {
            return true;
           }
           return false;
        }
        

        【讨论】:

        • 好吧,一旦我设置了 ServerCertificateValidationCallback,它就会影响所有未来的连接,所以我不确定如何通过 web.config 来实现。请举个例子。
        • 这可能适用于 HttpWebRequest,但我正在寻找专门针对 WCF 的设置。
        • 它可以通过使用http请求设置特定端点并标记删除门仅用于此端点来实现
        【解决方案7】:

        我在 Web.config 文件中使用了一个标志来指示证书何时是自签名的 而且我们也知道我们处于开发环境中:

        <add key="isSelfSignedCertificate" value="true"/>
        

        在代码中我们必须检查这个标志来应用证书验证异常:

        using System.Net;
        
        bool isSelfSignedCertificate = Convert.ToBoolean(ConfigurationManager.AppSettings["isSelfSignedCertificate"]);
        
        if (isSelfSignedCertificate)
        {
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-06-04
          • 2014-05-11
          • 2012-08-06
          • 2010-12-06
          • 2015-05-13
          • 2021-11-11
          • 2011-07-07
          相关资源
          最近更新 更多