Как избежать проверки сертификата при каждом вызове службы с помощью настраиваемой привязки WCF и настраиваемого валидатора - PullRequest
0 голосов
/ 28 февраля 2019

У меня есть служба 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создается один раз за сеанс (вход в конструктор вызывается один раз за сеанс).Но валидатор сертификата вызывается при каждом вызове службы.Как я могу заставить валидатор сертификата вызываться только в начале сеанса?

Учитывая, что он, похоже, использует сеансы, я предполагал, что проверка сертификата будет заполнена sesssion, и вызывался только один раз длясессия.Я просмотрел документацию по конфигурации 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>

1 Ответ

0 голосов
/ 11 марта 2019

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();
    }
}
...