[英]How to avoid cert validation on every service call with a WCF custom binding and a custom validator
我有使用自定义绑定和自定义证书验证器设置的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>
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;
}
}
我认为会话持续时间远小于证书生存期,并且如果证书被吊销,您还有其他方法可以终止会话:)
为了使情况更好,您可以添加最后一次验证调用的某种时间戳,如果合理的超时时间(例如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.