I am attempting to set up cert-based client authentication in a WCF service. I am using .NET 4.7.2.
I am testing both the host app and the client on the same machine. The intended behaviour is that the client uses a cert retrieved from a machine store to authenticate against the service.
I can see that channelFactory does have the desired certificate set. I can see that it has a private key set, when I view it via certmgr. I can see that its root cert is in the machine's root store.
This is how the client connects:
EndpointIdentity identity = EndpointIdentity.CreateDnsIdentity("serviceDnsId");
EndpointAddress address = new EndpointAddress(new System.Uri("net.tcp://192.168.64.222:8747/blahblah"), identity, new AddressHeaderCollection());
Service = serviceChannelFactory.CreateChannel(address);
channelFactory.Credentials.ClientCertificate.SetCertificate(System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine, System.Security.Cryptography.X509Certificates.StoreName.My, System.Security.Cryptography.X509Certificates.X509FindType.FindByThumbprint, "e1f3bd637d26df83361865bd85d046006c868546");
channelFactory.CreateChannel(address)
This is how the service is set up:
SecurityBindingElement security = SecurityBindingElement.CreateCertificateOverTransportBindingElement();
elements.Add(security);
SslStreamSecurityBindingElement sslStreamSecurityBE = new SslStreamSecurityBindingElement();
sslStreamSecurityBE.SslProtocols = SslProtocols.Tls12;
elements.Add(sslStreamSecurityBE);
CustomBinding binding = new CustomBinding(elements);
priceSinkSSLHost.AddServiceEndpoint(
typeof(IPriceSinkSSLService),
binding,
@"net.tcp://localhost:8747/blahblah/");
setCertificate(serviceSSLHost);
serviceSSLHost.Open();
I have removed a lot of code from both of the above that I understand to be unrelated.
The connection fails with the message "An error occurred when verifying security for the message". I don't know what is wrong or how to investigate it.
As suggested by stuartd, I added a security audit behavior. I did this programmatically as follows:
var audit = new ServiceSecurityAuditBehavior();
audit.AuditLogLocation = AuditLogLocation.Application;
audit.ServiceAuthorizationAuditLevel = AuditLevel.SuccessOrFailure;
audit.MessageAuthenticationAuditLevel = AuditLevel.SuccessOrFailure;
audit.SuppressAuditFailure = false;
priceSinkSSLHost.Description.Behaviors.Add(audit);
If the security isn't set in the client, the following is logged:
MessageSecurityException: Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security.
If it is set as above, then nothing is logged and it doesn't connect.