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