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?
Related
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?
I'm writing a client against a customer's SOAP service, using WCF.
We've had a number of go-arounds trying to get the authentication working. I ended up using a Custom Binding, because some random guy on the web said that BasicHttpBinding didn't support the necessary security options, and WsHttpBinding didn't support SOAP 1.1, which is what they are using.
So, what I have:
var message = this.constructMessagecollection);
if (message != null)
{
var ea = new EndpointAddress(this.webServiceUrl);
var binding = new CustomBinding();
binding.Elements.Add(new TextMessageEncodingBindingElement(
MessageVersion.Soap11, Encoding.UTF8));
binding.Elements.Add(new HttpsTransportBindingElement { AuthenticationScheme = System.Net.AuthenticationSchemes.Basic });
using (var client = new CustomersWebserviceClient(binding, ea))
{
if (!String.IsNullOrWhiteSpace(this.webServiceUsername) && !String.IsNullOrWhiteSpace(this.webServicePassword))
{
var credentials = client.ClientCredentials.UserName;
credentials.UserName = this.webServiceUsername;
credentials.Password = this.webServicePassword;
}
var result = client.ReceiveMessage(message);
log.writeLine(String.Format("Call to client.ReceiveMessage() returned {0}", result));
}
return true;
}
Now, I've been asked if I can configure my client to do preemptive authentication. I've done some web browsing, and not found much. And I'm at a loss as to how to integrate what little I've found into my current code.
I don't think you can configure WCF to pre authenticate. Your options are to add the headers manually to each request or to build a message inspector to do it and configure it once. Either way those settings are not related to the binding. I guess you could write your own custom http transport (that internally uses the regular http transport) and add it there but not sure it worth the effort. As described here, to add it manually you can use:
HttpRequestMessageProperty httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[HttpRequestHeader.Authorization] = "Basic " +
Convert.ToBase64String(Encoding.ASCII.GetBytes(client.ClientCredentials.UserName.UserName + ":" +
client.ClientCredentials.UserName.Password));
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] =
httpRequestProperty;
// Invoke client
}
As for the second option, this see here how to add headers with a message inspector.
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;
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 ?
I have a wcf service I am setting up to run under IIS 7. I have the service set to streaming for the transfermode.
When I self host the service in a console application every thing seems to work ok. But when the client connects to an iis hosted service it seems to be buffering, and the client eventual times out. I have used fiddler to determine that this client time out happens before the http request is even made.
Here is the servers binding.
var binding = new CustomBinding();
binding.Elements.Add( new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.Soap12WSAddressing10
} );
var secBinding = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
secBinding.AllowInsecureTransport = true;
binding.Elements.Add( secBinding );
binding.Elements.Add( new HttpTransportBindingElement()
{
TransferMode = TransferMode.Streamed,
MaxReceivedMessageSize = Int32.MaxValue,
} );
And the client binding:
var binding = new CustomBinding();
binding.Elements.Add( new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.Soap12WSAddressing10
} );
var secBinding = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
secBinding.AllowInsecureTransport = true;
binding.Elements.Add( secBinding );
binding.Elements.Add( new HttpTransportBindingElement()
{
TransferMode = TransferMode.Streamed,
MaxReceivedMessageSize = Int32.MaxValue,
MaxBufferSize = 400
} );
As an aside the connection is timing out because the stream is infinite and the server should read the first few bytes and then close the stream.
Recently, we had the same issue. When you host your service under IIS, no matter if you enable streaming or not, your service will buffer the entire message prior to sending it. The reason for this, is that it appears as though WCF does not set the Response.BufferOutput to "false" (default is true), when streaming is enabled on a service. A workaround can be found here:
http://weblogs.asp.net/jclarknet/archive/2008/02/14/wcf-streaming-issue-under-iis.aspx
Are you closing the Stream in the client? If true, try closing just in the service side.
Also, verify if its a OneWay operation.
Can you post the both binding nodes, for the endpoints?