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

У меня тут странная ситуация. У меня заработало, но я не понимаю, почему. Ситуация следующая:

Существует служба WCF, которую должно вызывать мое приложение (веб-сайт). Служба WCF предоставляет netTcpBinding и требует Transport Security (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 по умолчанию для аутентификации, но разве он не делает этого в любом случае?

ОБНОВЛЕНИЕ
Итак, после дополнительных исследований и проб и ошибок я так и не нашел ответа на эту проблему. В некоторых случаях, если я не укажу <dns />, я получу ошибку Credentials rejected, но если я укажу <dns value="whatever"/>config, это сработает. Почему?


person RoelF    schedule 06.05.2010    source источник
comment
Такие проблемы SSPI с нашей связью WCF Client/Service в течение довольно долгого времени, но они никогда не могли решить их должным образом, и это происходило только с некоторыми пользователями на некоторых машинах. Идентификация DNS — даже пустая — кажется, делает галочку... просто вау.   -  person Robert Muehsig    schedule 25.10.2017


Ответы (3)


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

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

person Vitalik    schedule 06.05.2010
comment
Что ж, прочитав эту страницу еще раз и попробовав несколько разных конфигураций, проблема все еще не решена. Даже если я поставлю my-crappy-unexisting-dummy-domain.com , он все равно будет работать. Я подумал, что он должен попытаться сделать запрос, а затем вернуться к значению по умолчанию, но я не могу найти в журнале ничего, что указывало бы на то, что DNS-сервер или сервер приложений даже пытались пройти аутентификацию в этом домене... Итак, мой вопрос: еще не решен. Спасибо, что помогли мне! - person RoelF; 07.05.2010
comment
Я думаю, вам понадобится сертификат с доменным именем, чтобы клиент мог пройти аутентификацию. - person Vitalik; 07.05.2010
comment
+1 Привет, не могли бы вы объяснить мне это, так что это значит, если DNS имеет значение = localhost? - person Lamps; 10.11.2011

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

Из MSDN:

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

См. также "Пример удостоверения службы" в MSDN.

person Rana Ian    schedule 29.02.2012

Не ответ, но тот же «трюк» работает, если вы создаете 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(""));

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

person Robert Muehsig    schedule 25.10.2017