WCF SslStreamSecurity DNS Identity Check failing for just 4.6 framework
Asked Answered
S

4

11

I am working on developing a new binding for a Wcf service that is hosted in IIS, I thought I got everything working, but it turns out that the client only works when it is targetting .Net framework 4.5, if I change it to target 4.6 then I get the following error when I try to open a connection:

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)

If I do nothing other than change the target framework in my test code back to 4.5, then it works fine. This makes me think that it could be a bug in .Net 4.6, I know there were Wcf ssl changes made in 4.6

With first chance exceptions turned on I see the following exception that is raised internally in 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

The wcf service being communicated to is targeting 4.6, and as far as I can tell I am specifying the dns identity, which does exist as a CN= in the cert subject. The binding is a custom binding so that I can do federated net.tcp, the client creates everything in code and I don't use the Add Service Reference feature in visual studio, the client code that is creating the binding:

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]));

Could this be a bug in the 4.6 framework causing different behavior? Would the next steps only be trying to step through and debug framework code to try and find why 4.6 is behaving differently?

EDIT - I created a small sample project that demonstrates the error, the repro steps are:

  • (Using VS 2015) Open the WcfSelfHostedServer solution
  • Add the IdentityFail.pfx cert to your Local Computer, Personal store using mmc
  • Run the WcfSelfHostedServer project (likely clicking firewall yes allow port 30000)
  • Open the WcfClient solution
  • Right click on project > properties, note that it is targetting 4.6.1
  • Run the project, it will throw the exception described above
  • Now switch the client to target 4.5.2, it will run fine with no errors

Update - I found the following that appear related: https://support.microsoft.com/en-us/kb/3069494 https://msdn.microsoft.com/en-us/library/mt298998(v=vs.110).aspx

But specifying Tls12 at the server and client didn't fix the issue, and even adding the DontEnableSchUseStrongCrypto=true flag didn't affect the DNS Identity Check error even though it got around the Enum.Parse internal error that was being thrown from this line

Schoening answered 20/1, 2016 at 20:57 Comment(0)
S
18

I needed to look at Retargetting Changes in the .NET Framework 4.6.1, as certificate validation logic changed in that release. (change in behavior for X509CertificateClaimSet.FindClaims that was causing my issue)

The fix is editing my app.config to add:

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

You can see the changed code on referencesource, and naturally makecert.exe doesn't appear to support generating certificates with "Subject Alternative Name" fields

Schoening answered 21/1, 2016 at 1:59 Comment(0)
M
4

You can fix in code adding one line.

like this.

        AppContext.SetSwitch("Switch.System.IdentityModel.DisableMultipleDNSEntriesInSANCertificate",true);
Minotaur answered 2/8, 2016 at 15:41 Comment(2)
For some reason this worked and the app.config change did not.Illjudged
I'm experiencing the same thing. I set the switch in web.config's runtime section, but it doesn't get applied. Setting it in code works, though (and fixes the problem for me). I don't have a solution to this at the time being... just thought I'd chime in.Mufinella
C
2

Installing .net 4.7 on the server solved the problem for me.

Christoffer answered 28/9, 2017 at 8:11 Comment(0)
S
1

Brandon.

It appears that if the flag is 'false' AND a cert does NOT contain SAN entries, we don't add the dns entry.

Slider answered 27/1, 2016 at 22:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.