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

问题描述 投票:1回答:1

我有一个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设置如下。目标是使用传输安全性并使用会话。我希望在创建会话时验证cert一次。但是,我可以通过登录cert验证器看到,当使用现有的开放式WCF客户端代理时,验证将发生在客户端进行的每个服务调用上。

我已经验证了每个会话创建了一次我的WCF服务实例(每个会话都会调用一次构建函数的登录)。但是,每个服务调用都会调用cert验证器。如何才能在会话开始时调用cert验证器?

鉴于它似乎正在使用会话,我认为证书验证将是sesssion-full,并且每个会话只调用一次。我已经仔细阅读了MSDN上的WCF配置文档,并没有看到进一步定制reliableSession标记的方法,也没有看到与安全相关的任何内容来做我想做的事情。

这是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个回答
0
投票

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();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.