WCF SslStreamSecurity DNS身份验证仅适用于4.6框架失败

11
我正在开发一个新的绑定,用于托管在IIS中的Wcf服务。我以为一切都正常工作了,但事实证明,只有当客户端以 .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's distinguished name.
  Source=System.ServiceModel
  StackTrace:
       at System.ServiceModel.Security.IdentityVerifier.EnsureIdentity(EndpointAddress serviceReference, AuthorizationContext authorizationContext, String errorString)

如果我只是在我的测试代码中改变目标框架回到4.5版本,那么它可以正常工作。这让我认为可能是在 .Net 4.6 中的一个 bug,我知道在其中有 Wcf ssl changes made in 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框架中的一个bug导致了不同的行为?接下来的步骤只需尝试逐步调试框架代码以找出4.6行为差异的原因吗?

编辑 - 我创建了一个小样本项目,演示了错误,复现步骤如下:

  • (使用VS 2015)打开WcfSelfHostedServer解决方案
  • 使用mmc将IdentityFail.pfx证书添加到“本地计算机”的“个人”存储中
  • 运行WcfSelfHostedServer项目(可能需要允许端口30000通过防火墙)
  • 打开WcfClient解决方案
  • 右键单击项目>属性,注意它正在针对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内部错误
4个回答

18

我需要查看.NET Framework 4.6.1中的Retargetting更改,因为在该版本中证书验证逻辑发生了变化。(由于X509CertificateClaimSet.FindClaims行为的更改导致了我的问题)

解决方法是编辑我的app.config文件并添加:

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

您可以查看引用源上的更改代码, 而且自然而然地,makecert.exe似乎不支持使用“主题替代名称”字段生成证书


4
您可以通过添加一行代码来解决问题。
就像这样。
        AppContext.SetSwitch("Switch.System.IdentityModel.DisableMultipleDNSEntriesInSANCertificate",true);

1
由于某种原因,这个方法可行而更改app.config则不行。 - Jazaret
1
我也遇到了同样的问题。我在web.config的runtime部分设置了开关,但它没有被应用。不过在代码中设置可以解决问题(对我来说有效)。目前我还没有解决方案...只是想发表一下意见。 - erlingormar

2

在服务器上安装 .NET 4.7 解决了我的问题。


1

Brandon。

看起来如果标志是“false”,并且证书不包含SAN条目,我们就不会添加DNS条目。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接