WCF SslStreamSecurity Проверка подлинности DNS-идентификатора с ошибкой всего в 4,6 фреймах

Я работаю над созданием новой привязки для службы Wcf, размещенной в IIS, я думал, что у меня все работает, но оказывается, что клиент работает только тогда, когда он нацелен на .NET Framework 4.5, если я его изменю для целевого значения 4.6, тогда я получаю следующую ошибку при попытке открыть соединение:

System.ServiceModel.Security.MessageSecurityException occurred
  HResult=-2146233087
  Message=The Identity check failed for the outgoing message. The remote endpoint did not provide a domain name system (DNS) claim and therefore did not satisfied DNS identity 'xxx.domain.local'. This may be caused by lack of DNS or CN name in the remote endpoint X.509 certificate distinguished name.
  Source=System.ServiceModel
  StackTrace:
       at System.ServiceModel.Security.IdentityVerifier.EnsureIdentity(EndpointAddress serviceReference, AuthorizationContext authorizationContext, String errorString)

Если я ничего не делаю, кроме как изменить целевую структуру в моем тестовом коде обратно на 4.5, то он отлично работает. Это заставляет меня думать, что это может быть ошибкой в ​​.Net 4.6, я знаю, что были изменения Wcf ssl, сделанные в 4.6

При первом включении исключения я вижу следующее исключение, которое создается внутри System.ServiceModel

System.ArgumentNullException occurred
  HResult=-2147467261
  Message=Value cannot be null.
Parameter name: value
  ParamName=value
  Source=mscorlib
  StackTrace:
       at System.Enum.TryParseEnum(Type enumType, String value, Boolean ignoreCase, EnumResult& parseResult)
  InnerException: 

    System.ServiceModel.dll!System.ServiceModel.Security.IssuanceTokenProviderBase<System.ServiceModel.Security.Tokens.IssuedSecurityTokenProvider.FederatedTokenProviderState>.DoNegotiation(System.TimeSpan timeout)  Unknown     System.ServiceModel.dll!System.ServiceModel.Security.IssuanceTokenProviderBase<System.ServiceModel.Security.Tokens.IssuedSecurityTokenProvider.FederatedTokenProviderState>.GetTokenCore(System.TimeSpan timeout)   Unknown
    System.IdentityModel.dll!System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(System.TimeSpan timeout) Unknown
    System.ServiceModel.dll!System.ServiceModel.Security.Tokens.IssuedSecurityTokenProvider.GetTokenCore(System.TimeSpan timeout)   Unknown
    System.IdentityModel.dll!System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(System.TimeSpan timeout) Unknown
    System.ServiceModel.dll!System.ServiceModel.Security.SecurityProtocol.TryGetSupportingTokens(System.ServiceModel.Security.SecurityProtocolFactory factory, System.ServiceModel.EndpointAddress target, System.Uri via, System.ServiceModel.Channels.Message message, System.TimeSpan timeout, bool isBlockingCall, out System.Collections.Generic.IList<System.ServiceModel.Security.SupportingTokenSpecification> supportingTokens)    Unknown
    System.ServiceModel.dll!System.ServiceModel.Security.TransportSecurityProtocol.SecureOutgoingMessageAtInitiator(ref System.ServiceModel.Channels.Message message, string actor, System.TimeSpan timeout)    Unknown
    System.ServiceModel.dll!System.ServiceModel.Security.TransportSecurityProtocol.SecureOutgoingMessage(ref System.ServiceModel.Channels.Message message, System.TimeSpan timeout) Unknown
    System.ServiceModel.dll!System.ServiceModel.Security.SecurityProtocol.SecureOutgoingMessage(ref System.ServiceModel.Channels.Message message, System.TimeSpan timeout, System.ServiceModel.Security.SecurityProtocolCorrelationState correlationState)  Unknown
    System.ServiceModel.dll!System.ServiceModel.Channels.SecurityChannelFactory<System.ServiceModel.Channels.IRequestChannel>.SecurityRequestChannel.Request(System.ServiceModel.Channels.Message message, System.TimeSpan timeout) Unknown
    System.ServiceModel.dll!System.ServiceModel.Channels.TransactionRequestChannelGeneric<System.ServiceModel.Channels.IRequestChannel>.Request(System.ServiceModel.Channels.Message message, System.TimeSpan timeout)  Unknown
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.RequestChannelBinder.Request(System.ServiceModel.Channels.Message message, System.TimeSpan timeout)  Unknown
    System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.Call(string action, bool oneway, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation, object[] ins, object[] outs, System.TimeSpan timeout)  Unknown
    System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(System.Runtime.Remoting.Messaging.IMethodCallMessage methodCall, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation) Unknown
    System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.Invoke(System.Runtime.Remoting.Messaging.IMessage message) Unknown
    mscorlib.dll!System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(ref System.Runtime.Remoting.Proxies.MessageData msgData, int type) Unknown

Передаваемая услуга wcf нацелена на 4.6, и насколько я могу судить, я указываю идентификатор dns, который существует как CN = в теме сертификата. Связывание является настраиваемой привязкой, так что я могу сделать федеративный net.tcp, клиент создает все в коде, и я не использую функцию добавления ссылки на службу в visual studio, код клиента, который создает привязку:

var binding = new CustomBinding(new BindingElement[] {
            new TransactionFlowBindingElement(),
            security,
            new SslStreamSecurityBindingElement(),
            new BinaryMessageEncodingBindingElement() {
                ReaderQuotas = { MaxDepth = maxReceivedSizeBytes, MaxStringContentLength = maxReceivedSizeBytes, MaxArrayLength = maxReceivedSizeBytes, MaxBytesPerRead = maxReceivedSizeBytes, MaxNameTableCharCount = maxReceivedSizeBytes },
            },
            new TcpTransportBindingElement {
                TransferMode = TransferMode.StreamedResponse,
                MaxReceivedMessageSize = maxReceivedSizeBytes,
            },
        }) {
    SendTimeout = sendTimeout,
};

var channelFactory = new ChannelFactory<T>(binding, new EndpointAddress(new Uri(url), EndpointIdentity.CreateDnsIdentity("xxx.domain.local"), new AddressHeader[0]));

Может ли это быть ошибкой в ​​структуре 4.6, вызывающей другое поведение? Будут ли следующие шаги только пытаться выполнить и отладить код рамки, чтобы попытаться найти, почему 4.6 ведет себя по-другому?

РЕДАКТИРОВАТЬ - Я создал небольшой проект образца который демонстрирует ошибку, шаги воспроизведения:

  • (Использование VS 2015) Откройте решение WcfSelfHostedServer
  • Добавьте сертификат IdentityFail.pfx на свой локальный компьютер, личное хранилище, используя mmc
  • Запустите проект WcfSelfHostedServer (вероятно, щелчок на брандмауэре да разрешает порт 30000)
  • Откройте решение WcfClient
  • Щелкните правой кнопкой мыши по объектам проектa > , обратите внимание, что он нацелен на 4.6.1
  • Запустите проект, он выкинет исключение, описанное выше.
  • Теперь переключите клиента на цель 4.5.2, он будет работать нормально, без ошибок

Обновление - Я обнаружил следующее: https://support.microsoft.com/en-us/kb/3069494 https://msdn.microsoft.com/en-us/library/mt298998(v=vs.110).aspx

Но указание Tls12 на сервере и клиенте не устранило проблему, и даже добавление флага DontEnableSchUseStrongCrypto = true не повлияло на ошибку проверки подлинности DNS, даже если она обошла внутреннюю ошибку Enum.Parse, которая выбрасывается из этой строки

Ответ 1

Мне нужно было посмотреть Retargetting Changes в .NET Framework 4.6.1, так как в этом выпуске была изменена логика проверки сертификатов. (изменение поведения для X509CertificateClaimSet.FindClaims, которое вызывало мою проблему)

Исправлено редактирование моего app.config для добавления:

<runtime>
    <AppContextSwitchOverrides value="Switch.System.IdentityModel.DisableMultipleDNSEntriesInSANCertificate=true" /> 
</runtime>

Вы можете увидеть измененный код на sourcesource, и, естественно, makecert.exe не поддерживает генерирование сертификатов с полями "Тема альтернативного имени"

Ответ 2

Вы можете исправить код, добавив одну строку.

как это.

        AppContext.SetSwitch("Switch.System.IdentityModel.DisableMultipleDNSEntriesInSANCertificate",true);

Ответ 3

Brandon.

Похоже, что если флаг имеет значение "false", а сертификат НЕ содержит записи SAN, мы не добавляем запись dns.

Ответ 4

Установка .net 4.7 на сервере решила проблему для меня.