Как избежать проверки сертификата при каждом вызове службы с помощью настраиваемой привязки WCF и настраиваемого валидатора
У меня есть настроенная служба 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 создается один раз за сеанс (вход в конструктор вызывается один раз за сеанс). Но валидатор сертификата вызывается при каждом вызове службы. Как я могу заставить валидатор сертификата вызываться только в начале сеанса?
Учитывая, что он, похоже, использует сеансы, я предположил, что проверка сертификата будет полной сессией, и вызывался только один раз за сеанс. Я просмотрел документацию по конфигурации WCF на MSDN и не вижу способа дальнейшей настройки тега надежного сеанса или чего-либо, связанного с безопасностью, для выполнения того, что я хочу.
Вот 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>
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();
}
}