【问题标题】:How to avoid cert validation on every service call with a WCF custom binding and a custom validator如何避免使用 WCF 自定义绑定和自定义验证器对每个服务调用进行证书验证
【发布时间】:2019-07-22 14:10:13
【问题描述】:

我有一个设置了自定义绑定和自定义证书验证器的 WCF 服务。

证书验证器定义如下。稍后会扩展,但目前只是做一个基本的验证。

public class MyX509CertificateValidator : X509CertificateValidator
{
    private static readonly ILog Logger = LogManager.GetLogger(typeof(MyX509CertificateValidator));

    public MyX509CertificateValidator()
    {
        Logger.Info("certval - Constructor ");
    }

    public override void Validate(X509Certificate2 certificate)
    {
        Logger.Info("certval - Validate(). Calling Cert.validate()");
        bool verifyResult = certificate.Verify();
        Logger.Info("verify result: " + verifyResult);
        if (!verifyResult)
        {
            throw new SecurityTokenValidationException("cert had some bad juju");
        }
    }
}

我的 web.config 设置如下。目标是使用传输安全和使用会话。我希望在创建会话时验证一次证书。但是,我可以通过登录证书验证器看到,当使用现有的开放式 WCF 客户端代理时,客户端进行的每个服务调用都会进行验证。

我已验证我的 WCF 服务实例在每个会话中创建一次(在构造函数中的日志记录在每个会话中被调用一次)。但是,每次服务调用都会调用证书验证器。如何让证书验证器仅在会话开始时被调用?

鉴于它似乎正在使用会话,我假设证书验证将是会话完整的,并且每个会话仅调用一次。我仔细阅读了 MSDN 上的 WCF 配置文档,但没有看到进一步自定义可靠会话标签或任何与安全相关的内容来做我想做的事情的方法。

这是 web.config 和服务定义

    [ServiceBehavior(AutomaticSessionShutdown = true,
        InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class WcfBasicService : IWcfBasicService
    {

...

  <system.serviceModel>
    <bindings>

        <customBinding>  
            <binding name="reliableSessionOverHttps">  
                <reliableSession/>  
                <security authenticationMode="CertificateOverTransport"/>
                <httpsTransport />  
            </binding>  
        </customBinding>  
    </bindings>

    <services>
      <service name="WcfServiceLibrary1.WcfBasicService">
        <endpoint address="" binding="customBinding" contract="WcfServiceLibrary1.IWcfBasicService" name="mainEndpoint" 
            bindingConfiguration="reliableSessionOverHttps">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>

        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />

      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceCredentials>
            <clientCertificate>
              <authentication certificateValidationMode="Custom" customCertificateValidatorType="WcfServiceLibrary1.MyX509CertificateValidator, WcfServiceLibrary1" />
            </clientCertificate>
          </serviceCredentials>

          <!-- To avoid disclosing metadata information, 
          set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True" />
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="True" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

【问题讨论】:

    标签: wcf certificate custom-binding


    【解决方案1】:

    AFAIK 没有任何好的 WCF 配置解决方案,但是您可以在 Validate 方法中实现某种缓存,使用证书的 Thumbprint 属性(Thumbprint 实际上是证书正文的哈希):

    public class MyX509CertificateValidator : X509CertificateValidator
    {
        private static readonly ILog Logger = LogManager.GetLogger(typeof(MyX509CertificateValidator));
        private string lastValidCertTumbprint = null;
    
        public MyX509CertificateValidator()
        {
            Logger.Info("certval - Constructor ");
        }
    
        public override void Validate(X509Certificate2 certificate)
        {
            if ((lastValidCertTumbprint != null) && (certificate.Tumbprint == lastValidCertTumbprint)) 
            { 
              return; // Fast track
            } 
    
            Logger.Info("certval - Validate(). Calling Cert.validate()");
            bool verifyResult = certificate.Verify();
            Logger.Info("verify result: " + verifyResult);
            if (!verifyResult)
            {
                throw new SecurityTokenValidationException("cert had some bad juju");
            }
    
            // The cert valid, save this fact into fast track cache
            lastValidCertTumbprint = certificate.Tumbprint; 
        }
    }
    

    我假设会话持续时间远小于证书有效期,如果证书被吊销,您还有其他方法可以终止会话:)

    为了让事情变得更好,您可以添加最后一次验证调用的某种时间戳,并在合理的超时(例如 30 分钟)到期时重新验证证书:

    public class MyX509CertificateValidator : X509CertificateValidator
    {
        private static readonly ILog Logger = LogManager.GetLogger(typeof(MyX509CertificateValidator));
    
        private string    lastValidCertTumbprint = null;
        private Stopwatch lastValidCertTimeMarker = new Stopwatch();
        private const int VALIDATION_CACHE_LIFETIME = 30*60*1000; // in ms // 30min
    
        public MyX509CertificateValidator()
        {
            Logger.Info("certval - Constructor ");
        }
    
        public override void Validate(X509Certificate2 certificate)
        {
            if ((lastValidCertTumbprint != null) 
                && (certificate.Tumbprint == lastValidCertTumbprint)
                && (lastValidCertTimeMarker.ElapsedMilliseconds < VALIDATION_CACHE_LIFETIME))
            { 
                return;  // Fast track
            }
    
            lastValidCertTumbprint = null;
    
            Logger.Info("certval - Validate(). Calling Cert.validate()");
            bool verifyResult = certificate.Verify();
            Logger.Info("verify result: " + verifyResult);
            if (!verifyResult)
            {
                throw new SecurityTokenValidationException("cert had some bad juju");
            }
    
            // The cert valid, save this fact into fast track cache and save timestamp
            lastValidCertTumbprint = certificate.Tumbprint;
            lastValidCertTimeMarker.Reset();
            lastValidCertTimeMarker.Start();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-06
      • 2015-05-27
      • 1970-01-01
      • 2015-05-24
      • 2018-03-01
      • 2020-10-31
      • 1970-01-01
      • 2011-01-18
      相关资源
      最近更新 更多