Конечная точка клиента WCF: SecurityNegotiationException без <dns>

У меня странная ситуация. У меня это работает, но я не понимаю, почему. Ситуация такова:

Существует служба WCF, которую нужно вызвать моему приложению (веб-сайту). Служба WCF предоставляет netTcpBinding и требует безопасности транспорта (Windows). Клиент и сервер находятся в одном домене, но на разных серверах.
Таким образом, создание клиента приводит к следующей конфигурации (в основном по умолчанию)

<system.serviceModel>
    <bindings>
      <netTcpBinding>
         <binding name="MyTcpEndpoint" ...>          
              <reliableSession ordered="true" inactivityTimeout="00:10:00"
                              enabled="false" />
             <security mode="Transport">
                <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
                <message clientCredentialType="Windows" />
            </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <client> 
        <endpoint address="net.tcp://localhost:xxxxx/xxxx/xxx/1.0" 
                   binding="netTcpBinding" bindingConfiguration="MyTcpEndpoint" 
                   contract="Service.IMyService" name="TcpEndpoint"/>
    </client>
</system.serviceModel>

Когда я запускаю веб-сайт и звоню в службу, я получаю следующую ошибку:

System.ServiceModel.Security.SecurityNegotiationException: Either the target name is incorrect or the server has rejected the client credentials. ---> System.Security.Authentication.InvalidCredentialException: Either the target name is incorrect or the server has rejected the client credentials. ---> System.ComponentModel.Win32Exception: The logon attempt failed
    --- End of inner exception stack trace ---
    at System.Net.Security.NegoState.EndProcessAuthentication(IAsyncResult result)
    at System.Net.Security.NegotiateStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
    at System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeInitiator.InitiateUpgradeAsyncResult.OnCompleteAuthenticateAsClient(IAsyncResult result)
    at System.ServiceModel.Channels.StreamSecurityUpgradeInitiatorAsyncResult.CompleteAuthenticateAsClient(IAsyncResult result)
    --- End of inner exception stack trace ---

Server stack trace: 
    at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
    at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
    at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
....

Теперь, если я просто изменяю конфигурацию клиента так:

    <endpoint address="net.tcp://localhost:xxxxx/xxxx/xxx/1.0" 
               binding="netTcpBinding" bindingConfiguration="MyTcpEndpoint" 
               contract="Service.IMyService" name="TcpEndpoint">
        <identity>
            <dns />
        </identity> 
  </endpoint>

все работает, и мой сервер с радостью сообщает, что он вызван учетной записью службы, на которой размещается AppPool для моего сайта. Все хорошо.

Теперь мой вопрос: почему это работает? Что это делает? Я добрался до этого решения простой пробной ошибкой. Мне кажется, что все теги <dns /> указывают, что клиент использует DNS по умолчанию для аутентификации, но разве это не так?

UPDATE
Поэтому после нескольких исследований и проб и ошибок я до сих пор не нашел ответа на эту проблему. В некоторых случаях, если я не предоставляю <dns />, я получаю ошибку Credentials rejected, но если я предоставляю конфигурацию <dns value="whatever"/>, она работает. Почему?

Ответы

Ответ 1

Тег

<dns/> позволяет клиенту проверить идентификатор сервера. Например, если вы сказали <dns value="google.com"/>, он будет проверять, что сервер WCF предоставляет идентификатор google.com. Поскольку вы говорите <dns/>, это, вероятно, просто позволяет вам обслуживать вас.

Дополнительная информация на Идентификация службы и аутентификация

Ответ 2

MSDN "Идентификация службы и аутентификация" объясняет, что раздел идентификации конечных точек позволяет защитить безопасность на стороне клиента от фишинговых схем.

Из MSDN:

После того, как клиент инициирует связь с конечной точкой и служба аутентифицирует себя клиенту, клиент сравнивает значение тождества конечной точки с фактическим значением конечной точки процесс аутентификации возвращен. Если они совпадают, клиент уверен он связался с ожидаемой конечной точкой обслуживания. Это работает как защиты от фишинга, не позволяя клиенту быть перенаправляется на конечную точку, размещенную вредоносным сервисом.

Также см. MSDN Пример идентификации службы.

Ответ 3

Не ответ, но тот же "трюк" работает, если вы создаете EndpointAddress через код:

// does fail on some machines for some users 
// (I have no explanation here - just crazy)
var address = new EndpointAddress(new Uri(url));

// will work and the dns entry doesn't matter
address = new EndpointAddress(new Uri(url), UpnEndpointIdentity.CreateDnsIdentity(""));

Странно, и я не знаю, почему это работает, но, похоже, помогает.