Пользовательская проверка сертификата WCF — отправка сообщения об исключении клиенту

#c# #wcf #wcf-security

#c# #wcf #wcf-безопасность

Вопрос:

У меня есть простая служба WCF, настроенная для обеспечения безопасности транспорта и учетных данных клиента сертификата. Я подключаю к нему средство проверки пользовательского сертификата и выдает исключение FaultException. Моя проблема заключается в том, что сообщение esception не доходит до клиента: я получаю только исключение MessageSecurityException без какой-либо ошибки… Если я включу свою службу в net.tcp (самостоятельно размещенную), я получу сообщение об ошибке, также без сбоев.

Вот часть моего web.config :

 <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpEndpointBinding" messageEncoding="Text">
          <reliableSession enabled="false" />
          <security mode="Transport">
            <transport clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="SecureService.Server.ChunkStreamService">
        <endpoint binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" behaviorConfiguration="myBehavior" name="wsHttpEndPoint" contract="SecureService.Server.IChunkStreamService" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="myBehavior">
          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <clientCertificate>
              <authentication  customCertificateValidatorType="SecureService.Server.CPSValidator, SecureService.Server" certificateValidationMode="Custom" />
            </clientCertificate>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
  

и мой валидатор :

     public class CPSValidator : System.IdentityModel.Selectors.X509CertificateValidator
  {
    public override void Validate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate)
    {
       throw new FaultException("Certificate is not from a trusted issuer");
    }
  }
  

Есть идеи?

Заранее спасибо за любую помощь!

Комментарии:

1. AFAIK этап проверки сертификата выполняется на другом уровне — намного ниже механизмов обработки ошибок WCF, это происходит на уровне SSL, который в данном случае находится непосредственно поверх TCP / IP…

2. Но я прочитал в msdn (что, очевидно, не работает) «Примечание: чтобы вернуть ошибки аутентификации обратно клиенту, создайте исключение FaultException в методе Validate». ( msdn.microsoft.com/en-us/library/ms733806.aspx )

3. Я провел тест с аутентификацией по имени пользователя и пользовательским валидатором: отправка FaultException позволяет повторно получить сообщение на стороне клиента… Разница с моей проверкой сертификата заключается в том, что я использую TransportWithMessageCredential : может быть, в этом смысл?

4. что-то вроде этого: аутентификация имени пользователя происходит на уровне сервиса, в то время как проверка сертификата происходит на более низком уровне (таким образом, на сетевом уровне, который ничего не знает о пользовательских исключениях)…

5. Хорошо: итак, есть ли способ выполнить проверку подлинности TransportWithMessageCredential и сертификата? Снизит ли это уровень безопасности?

Ответ №1:

Насколько я знаю, любое исключение, которое вы создаете в валидаторе, никогда не доходит до клиента. Клиент всегда получает исключение MessageSecurityException. Клиенту правильно отправляются только исключения, которые происходят на уровне сервиса (в реализации сервиса) и сопоставляются с FaultContract.

С уважением, Пабло.

Комментарии:

1. следовательно, нет способа объяснить клиенту, что произошло? (истекший / отозванный / ненадежный … сертификат или другая неизвестная проблема)

Ответ №2:

Как ответили Яхия и Пабло, проверка сертификата не выполняется на уровне обслуживания. Поэтому FaultException не может достичь клиента. Но это верно только потому, что я использую режим безопасности транспорта. Чтобы отправить сообщение обратно клиенту, мне пришлось изменить режим безопасности с Transport на TransportWithMessageCredentials, например :

 <security mode="TransportWithMessageCredential">
  <transport clientCredentialType="None" proxyCredentialType="None" realm="" />
  <message clientCredentialType="Certificate" negotiateServiceCredential="true" algorithmSuite="Default" />
</security>
  

Таким образом, проверка сертификата выполняется на уровне сервиса, так что сообщение об ошибке может быть отправлено клиенту через FaultException.

Ответ №3:

Вы пробовали включить диагностику WCF на сервере, чтобы выявить проблемы в конвейере WCF до того, как вызов попадет в саму службу?

 <system.diagnostics>
<sources>
  <source name="System.ServiceModel" switchValue="Verbose" propagateActivity="true">
    <listeners>
      <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="log.evtlog"  />
    </listeners>
  </source>
</sources>
<trace autoflush="true"/>
  

Комментарии:

1. К сожалению, это не дает мне больше информации о том, что происходит, и почему мои сообщения об ошибках не поступают на сторону клиента. Похоже, Яхия и Пабло правы, когда говорят, что FaultContract существует только на уровне обслуживания.