WCF Security exception when calling from Windows Service - wcf

I have some code which consumes a WCF service. The service is protected by basic authentication, so on creating the client, I'm using the following code:
BasicHttpBinding httpBinding = new BasicHttpBinding();
httpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
httpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
httpBinding.Security.Transport.Realm = service_realm;
EndpointAddress address = new EndpointAddress(service_address);
Service.ServiceClient client = new Service.ServiceClient(httpBinding, address);
client.ClientCredentials.UserName.UserName = service_username;
client.ClientCredentials.UserName.Password = service_password;
Works fine when I run the code from a console app. But when I run the same code from a windows service, a MessageSecurityException is being thrown telling me that my request was unauthorized. For some reason it seems to be using the current Windows account for authentication, because my own account does have access to the service. But I don't want it to, I want it to use the stored credentials. What am I missing here?

WCF basicHttpBinding does not support plaintext credentials; the reason is because the moment you want pass credentials around on a transport binding, WCF requires the underlying transport to be a secure transport, such as SSL.
In order for your code to work, you would then need to use service via https or using certificates or encryption.

Seems to be fixed using this config:
_httpBinding = new BasicHttpBinding();
_httpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
_httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
_httpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
_httpBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
_httpBinding.Security.Message.AlgorithmSuite = SecurityAlgorithmSuite.Default;
_httpBinding.AllowCookies = false;
_httpBinding.BypassProxyOnLocal = false;
_httpBinding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
_httpBinding.MessageEncoding = WSMessageEncoding.Text;
_httpBinding.TextEncoding = Encoding.UTF8;
_httpBinding.TransferMode = TransferMode.Buffered;
_httpBinding.UseDefaultWebProxy = false;
Service.ServiceClient client = new Service.ServiceClient(_httpBinding, _address);
client.ClientCredentials.UserName.UserName = service_username;
client.ClientCredentials.UserName.Password = service_password;

Related

Configuration for ClientCredentials ServiceCertificate authentication not applied or used

I am building a .NET Core 3.1 application where I am trying to call a WCF Service over HTTPS and temporarily disabling SSL authentication for the server certificate.
There is a clearly documented way to achieve this. Namely, by setting the ServiceCertificate.SslCertificateAuthentication property on the ChannelFactory class.
Below is code for setting up het Binding, endpoint and ClientCredentials.
var endpointAddress = new EndpointAddress("https://*.com");
var binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.Security.Transport = new HttpTransportSecurity()
{
ClientCredentialType = HttpClientCredentialType.None
};
var factory = new ChannelFactory<IService>(binding, endpointAddress);
factory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication()
{
CertificateValidationMode = X509CertificateValidationMode.None,
RevocationMode = X509RevocationMode.NoCheck
};
factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
factory.Credentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;
var client = factory.CreateChannel();
client.Call();
However, when I run this code I receive the exception chain:
Could not establish trust relationship for the SSL/TLS secure channel
with authority 'domain'
The SSL connection could not be established,
see inner exception.
Authentication failed, see inner exception. The
message received was unexpected or badly formatted.
I would expect the WCF client to have skipped SSL authentication.
I also tried to use a custom certificate validator, by extending the X509CertificateValidator and configuring this in the following way:
factory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication()
{
CertificateValidationMode = X509CertificateValidationMode.Custom,
CustomCertificateValidator = new CustomCertificateValidator();
};
factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.Custom;
factory.Credentials.ServiceCertificate.Authentication.CustomCertificateValidator = new CustomCertificateValidator();
As you might expect as this point, I receive the same exceptions as before. Even worse though, my CustomCertificate.Validate(..) method was not being called at all.
WCF seems to provide an API which allows for quite a bit of control but no matter what I try, my policies/configurations do not seem to by honoured in any way.
What might be going on here?
The below code will work when requiring the SSL authentication in a DotCore project.
Uri uri = new Uri("https://vabqia969vm:21011");
BasicHttpsBinding binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, new EndpointAddress(uri));
factory.Credentials.ServiceCertificate.SslCertificateAuthentication = new System.ServiceModel.Security.X509ServiceCertificateAuthentication()
{
CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None,
RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck
};
//these two lines will not work.
//factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
//factory.Credentials.ServiceCertificate.Authentication.RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck;
var client = factory.CreateChannel();
var result = client.TestAsync();
Console.WriteLine(result.Result);
On my side, it works perfectly. I think there is something wrong with the server-side. As you know, we should ensure that the binding type between the client-side and the server-side is consistent. What are the details on the server-side?

getting "Could not establish trust relationship for the SSL/TLS secure channel with authority" when running fiddler

I have a WCF client application that sends a https request to a third-party webservice. everythings works well. I receive the correct answer.
I have installed fiddler and trusted its rootcertificate to capture and encode the https traffic. But now my requests recieve the error "Could not establish trust relationship for the SSL/TLS secure..."
I have searched a lot on this error on the web. But each time the solution is that I need to trust the fiddler root certificate. Since I have done this from het start, it looks like this is not the solution for my problem.
Here is a snippet out of my code that sends the request:
AsymmetricSecurityBindingElement securityBindingElement = new AsymmetricSecurityBindingElement();
securityBindingElement.InitiatorTokenParameters = new X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never };
securityBindingElement.RecipientTokenParameters = new X509securityTokenParameters();
securityBindingElement.EnableUnsecuredResponse = true;
securityBindingElement.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());
CustomBinding binding = new CustomBinding(securityBindingElement, new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8), new HttpsTransportBindingElement());
EndpointAddress endpoint = new EndpointAddress(new Uri(URL), new X509CertificateEndpointIdentity(serverCertificate));
ChannelFactory<type> channelFactory = new ChannelFactory<type>(binding, endpoint);
channelFactory.Credentials.ClientCertificate.Certificate = clientX509Certificaat;
channelFactory.Credentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;
channelFactory.Endpoint.Contract.ProtectionLevel = ProtectionLevel.Sign;
type client = channelFactory.CreateChannel();
client.request();
Somebody knows what is what is going wrong here?
I changed my code to:
AsymmetricSecurityBindingElement securityBindingElement = new AsymmetricSecurityBindingElement();
securityBindingElement.InitiatorTokenParameters = new X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never };
securityBindingElement.RecipientTokenParameters = new X509securityTokenParameters();
securityBindingElement.EnableUnsecuredResponse = true;
securityBindingElement.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());
CustomBinding binding = new CustomBinding(securityBindingElement, new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8), new HttpsTransportBindingElement());
EndpointAddress endpoint = new EndpointAddress(new Uri(URL));
ChannelFactory<type> channelFactory = new ChannelFactory<type>(binding, endpoint);
channelFactory.Credentials.ClientCertificate.Certificate = clientX509Certificaat;
channelFactory.Credentials.ServiceCertificate.DefaultCertificate = serverX509Certificaat;
channelFactory.Endpoint.Contract.ProtectionLevel = ProtectionLevel.Sign;
type client = channelFactory.CreateChannel();
client.request();
It is not clear for me why I do not have problems anymore. Anyone?

WCF: Invalid Security Header

Usually when I consume a web service I add a service reference, put in the URL for the WSDL, and then finagle my way through the API's.
This time around I get a FaultException with the message: "Invalid security header".
Here is my binding:
CustomBinding bindingBNP = new CustomBinding();
SecurityBindingElement securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
securityElement.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128;
securityElement.KeyEntropyMode = System.ServiceModel.Security.SecurityKeyEntropyMode.CombinedEntropy;
securityElement.IncludeTimestamp = false;
securityElement.SecurityHeaderLayout = SecurityHeaderLayout.Lax;
MtomMessageEncodingBindingElement mtomElement = new MtomMessageEncodingBindingElement(MessageVersion.Soap11WSAddressing10, Encoding.UTF8);
HttpsTransportBindingElement httpsElement = new HttpsTransportBindingElement();
httpsElement.AuthenticationScheme = System.Net.AuthenticationSchemes.Anonymous;
httpsElement.BypassProxyOnLocal = false;
httpsElement.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
httpsElement.ManualAddressing = false;
httpsElement.ProxyAuthenticationScheme = System.Net.AuthenticationSchemes.Anonymous;
httpsElement.TransferMode = TransferMode.Buffered;
httpsElement.UnsafeConnectionNtlmAuthentication = false;
httpsElement.RequireClientCertificate = false;
httpsElement.UseDefaultWebProxy = false;
bindingBNP.Elements.Add(securityElement);
bindingBNP.Elements.Add(mtomElement);
bindingBNP.Elements.Add(httpsElement);
Related question: for diagnostic purposes, how do I know what the inbound/outbound communication is?
Fiddler doesn't seem to pick up anything (I guess it would have to be on the server machine, which I'm probably not going to be able to negotiate). WCF tracing only seems to surface communication "milestones" (if that word connotates some flavor of victory I am ways off!).
Fiddler runs on the client:
Did you start Fiddler before your client?
Did you configure your application to proxy its traffic?
Are you sure your WCF is using HTTP as the transport?

Consuming Teamcity REST API using WCF

I use following code to create a channel:
var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
basicHttpBinding.Security.Transport.Realm = "TeamCity";
var channelFactory = ChannelFactory<IMyInterface>(basicHttpBinding);
channelFactory.EndPint.Address = new EndPoint("http://localhost/httpAuth/app/rest/version");
channelFactory.Credentials.Username.Username = 'username';
channelFactory.Credentials.Username.Password = 'password';
channelFactory.Open();
var channel = channel.CreateChannel();
var verInfo = channel.GetVersion();
but when I run this code, CLR raise "The remote server returned an unexpected response:(405 Method not allowed)"
how can I do basic http authentication with wcf?

WCF : Configuring message security programmatically

I'm coding an Azure WCF Service Bus service, which is to be configured programmatically to have message security using certificates:
ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.Tcp;
// create the service URI based on the service namespace
Uri address = ServiceBusEnvironment.CreateServiceUri("sb", ConfigurationManager.AppSettings["serviceNamespace"], "TestService");
// create the credentials object for the endpoint
TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
sharedSecretServiceBusCredential.TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(ConfigurationManager.AppSettings["issuerName"], ConfigurationManager.AppSettings["issuerSecret"]);
//Create and bind the serviceEndpoint
ContractDescription contractDescription = ContractDescription.GetContract(typeof(ITestContract), typeof(TestServiceImpl));
ServiceEndpoint serviceEndPoint = new ServiceEndpoint(contractDescription);
serviceEndPoint.Address = new EndpointAddress(address);
var NetTcpRelayBinding = new NetTcpRelayBinding(EndToEndSecurityMode.TransportWithMessageCredential, RelayClientAuthenticationType.RelayAccessToken);
NetTcpRelayBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate; //The serivice will check the TrustedPeople store for the client
serviceEndPoint.Binding = NetTcpRelayBinding;
serviceEndPoint.Behaviors.Add(sharedSecretServiceBusCredential);
Host = new ServiceHost(typeof(TestServiceImpl), address);
//Add a service certificate
Host.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerTrust;
Host.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine,StoreName.My,X509FindType.FindByThumbprint,"E86870F0118CE39D771A49B9337C28444F3C7348");
// create the service host reading the configuration
Host.Description.Endpoints.Add(serviceEndPoint);
I can get this service up and running, however, any client )with just the ServiceBus SharedSecret, clientCredentials NOT set to use any cert) is able to call my service without any errors.
Is the above code sufficient to indicate that certificates (and only certificates base authorization) should be used for message security ?
Any good articles on configuring WCF message security programmatically ?
Turns out that lack of sleep was the culprit; I was running an older version of the service. Clients without any certificates do error out (with System.ServiceModel.ProtocolException was unhandled Message=Error while reading message framing format at position 1 of stream (state: Start).
A properly coded up client for this is :
ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.Tcp;
string serviceNamespace = "valid-namespace";
string issuerName = "owner";
string issuerSecret = "validSecret";
// create the service URI based on the service namespace
Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, "valid-namespace");
// create the credentials object for the endpoint
TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
sharedSecretServiceBusCredential.CredentialType = TransportClientCredentialType.SharedSecret;
sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = issuerName;
sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret = issuerSecret;
ChannelFactory<ITestChannel> channelFactory = new ChannelFactory<ITestChannel>();
channelFactory.Endpoint.Address = new EndpointAddress(serviceUri);
var NTRB = new NetTcpRelayBinding();
NTRB.Security.Mode = EndToEndSecurityMode.TransportWithMessageCredential;
NTRB.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
channelFactory.Endpoint.Binding = NTRB;
channelFactory.Endpoint.Contract.ContractType = typeof(ITestChannel);
// apply the Service Bus credentials
channelFactory.Endpoint.Behaviors.Add(sharedSecretServiceBusCredential);
//Question : Why doesn't use of the following line effect Service-Validation ? I can successfully call the service from a machine where the server's certificate does NOT exist in the trusted-people store
//channelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.PeerTrust;
channelFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "valid-thubmprint");
// create and open the client channel
ITestChannel channel = channelFactory.CreateChannel();
Console.WriteLine(channel.ServiceMethod());
Console.ReadKey();
channel.Close();
channelFactory.Close();
Still have the problem of the ServiceCertificate always being assumed valid, even when PeerTrust is used for channelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode and the service certificate isn't in the TrustedPeople store.
Anyone with ideas on why this happens ?