简体   繁体   English

如何避免使用WCF自定义绑定和自定义验证程序对每个服务调用进行证书验证

[英]How to avoid cert validation on every service call with a WCF custom binding and a custom validator

I have a WCF servcie set up with custom binding and a custom cert validator. 我有使用自定义绑定和自定义证书验证器设置的WCF服务。

THe cert validator is defined as follows. 证书验证者的定义如下。 It will be expanded later, but is just doing a basic verification currently. 稍后将对其进行扩展,但目前仅在进行基本验证。

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");
        }
    }
}

My web.config is set up as follows. 我的web.config设置如下。 The goal is to use Transport security and use sessions. 目标是使用传输安全性并使用会话。 I want the cert to be validated once, when the session is being created. 我希望在创建会话时对证书进行一次验证。 However, I can see through logging in the cert validator, that the validation takes place for every service call that a client makes, when using an existing open WCF client proxy. 但是,通过登录证书验证器,我可以看到,使用现有的开放WCF客户端代理时,验证是针对客户端进行的每个服务调用进行的。

I've verified that my WCF service instance is created once per session (logging in the constructor is being called once per session). 我已经验证了我的WCF服务实例是每个会话创建一次的(登录构造函数是每个会话调用一次)。 But, the cert validator is being called every single service calls. 但是,每个服务调用都会调用证书验证器。 How can I get the cert validator to be called only at the start of a session? 如何才能仅在会话开始时调用证书验证器?

Given that it appears to be using sessions, I assumed that the cert verification would be sesssion-full, and invoked just once per session. 鉴于它似乎正在使用会话,因此我假设证书验证是完整的,并且每个会话仅调用一次。 I've perused the WCF configuration documentation on MSDN and do not see a way to further customize reliableSession tag, or anything related to Security to do what I wish. 我已经仔细阅读了MSDN上的WCF配置文档,但没有找到进一步自定义可靠会话标记的方法,或与安全相关的任何事情来实现我希望的方法。

Here's the web.config and the service definition 这是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>

AFAIK there is no any good WCF configuration solution for that, but you may implement some sort of cache inside Validate method, using Thumbprint property of the certificate ( Thumbprint is actually hash of the certificate body): AFAIK对此没有任何好的WCF配置解决方案,但是您可以使用证书的Thumbprint属性在Validate方法中实现某种缓存( 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; 
    }
}

I assume that session duration is far less then certificate lifetime and in case certificate(s) revoked, you have other means to terminate the sessions :) 我认为会话持续时间远小于证书生存期,并且如果证书被吊销,您还有其他方法可以终止会话:)

To made things better, you may add some sort of timestamp of the last validation call and re-validate certificate if reasonable timeout (say, 30min) expired: 为了使情况更好,您可以添加最后一次验证调用的某种时间戳,如果合理的超时时间(例如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();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM