I'm having difficulty connecting to a 3rd party WSE 3.0 web service from a WCF client. I have implemented the custom binding class as indicated in this KB article:
http://msdn.microsoft.com/en-us/library/ms734745.aspx
The problem seems to have to do with the security assertion used by the web service - UsernameOverTransport.
When I attempt to call a method, I get the following exception:
System.InvalidOperationException: The
'WseHttpBinding'.'[namespace]'
binding for the
'MyWebServiceSoap'.'[namespace]'
contract is configured with an
authentication mode that requires
transport level integrity and
confidentiality. However the transport
cannot provide integrity and
confidentiality..
It is expecting a username, password, and CN number. In the example code supplied to us by the vendor, these credentials are bundled in a Microsoft.Web.Services3.Security.Tokens.UsernameToken. Here's the example supplied by the vendor:
MyWebServiceWse proxy = new MyWebServiceWse();
UsernameToken token = new UsernameToken("Username", "password", PasswordOption.SendPlainText);
token.Id = "<supplied CN Number>";
proxy.SetClientCredential(token);
proxy.SetPolicy(new Policy(new UsernameOverTransportAssertion(), new RequireActionHeaderAssertion()));
MyObject mo = proxy.MyMethod();
This works fine from a 2.0 app w/ WSE 3.0 installed. Here is a snippet of the code from my WCF client:
EndpointAddress address = new EndpointAddress(new Uri("<web service uri here>"));
WseHttpBinding binding = new WseHttpBinding(); // This is the custom binding I created per the MS KB article
binding.SecurityAssertion = WseSecurityAssertion.UsernameOverTransport;
binding.EstablishSecurityContext = false;
// Not sure about the value of either of these next two
binding.RequireDerivedKeys = true;
binding.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt;
MembershipServiceSoapClient proxy = new MembershipServiceSoapClient(binding, address);
// This is where I believe the problem lies – I can’t seem to properly setup the security credentials the web service is expecting
proxy.ClientCredentials.UserName.UserName = "username";
proxy.ClientCredentials.UserName.Password = "pwd";
// How do I supply the CN number?
MyObject mo = proxy.MyMethod(); // this throws the exception
I've scoured the web looking for an answer to this question. Some sources get me close (like the MS KB article), but I can't seem to get over the hump. Can someone help me out?
I had success in a similar case with the following binding configuration:
<bindings>
<customBinding>
<binding name="FNCEWS40MTOMBinding">
<security enableUnsecuredResponse="true" authenticationMode="UserNameOverTransport"
allowInsecureTransport="true" messageProtectionOrder="SignBeforeEncrypt">
<secureConversationBootstrap />
</security>
<mtomMessageEncoding messageVersion="Soap12WSAddressingAugust2004"
maxBufferSize="2147483647" />
<httpTransport maxReceivedMessageSize="2147483647" />
</binding>
</customBinding>
</bindings>
Hope it works for you too.
The error message is refering to Transport Level Security, this usually means https.
You have not shown your configuration files. But I am guessing that you have configured security to be transport (or it is required as a consiquence of another choice) and used an address that is http instead of https.
Related
My particular problem is something like this:
We are currently running a set of services which requires the clients to provide a username and password as authentication when calling the services.
We would like to implement a PKI-infrastructure on these services, but some of our partners will use longer time to accommodate to this new infrastructure than the others.
As a first step we want to require client certificates from some of our partners. A client certificate will be required (in addition to username and password) to access their data on our servers, while for the other users only username and password will be required.
To solve this problem I am trying to implement a custom validator for both the username/password authentication (using UserNamePasswordValidator) and for the client certificates (using X509CertificateValidator) in WCF. The username/password validator will verify these credentials towards our database, while the client certificate validator will inspect whether the request is from a client from which we require a certificate, and if so verify that a valid client certificate is provided. I have not been able to configure WCF so that it uses both of these validators.
My WCF configuration on the server is currently set up like this:
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpsGetEnabled="true" policyVersion="Policy15" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<clientCertificate>
<authentication customCertificateValidatorType="MyWS.Security.MyServicesCertificateValidator, MyWS"
certificateValidationMode="Custom" revocationMode="NoCheck" />
</clientCertificate>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="MyWS.Security.MyServicesUsernameValidator, MyWS" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
<bindings>
<basicHttpBinding>
<binding name="MySoapBinding">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Certificate" />
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="MyWS.Services.TheService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="MySoapBinding" name="TheService" bindingNamespace="https://services.my/TheService" contract="MyWS.Interfaces.Service.ITheService" />
<host>
<baseAddresses>
<add baseAddress="https://localhost:4434/MyWS/TheService"/>
</baseAddresses>
</host>
</service>
</services>
As far as I understand this configuration is invalid because I can't use the customCertificateValidatorType at the transport layer (because IIS inspects the certificate before WCF is involved here), but I can not see how I am able to combine both the customCertificateValidatorType and customUserNamePasswordValidatorType as credential types at the message layer either.
I have implemented a message inspector and might be able to solve the problem using the OperationContext in some way (as suggested in the link below), but I have not been able to see a way for me to do it this way yet.
http://social.msdn.microsoft.com/Forums/en/wcf/thread/b6ab8b58-516b-41d4-bb0e-75b4baf92716
I suppose I might be trying to implement something that is incompatible with the way WCF works, but if someone have an idea about how this could be fixed I would be delighted to have your feedback on this.
I think I have found a solution to my problem now thanks to valuable input from #ladislav-mrnka in his answer. I realized it is necessary to provide two endpoints to configure the different requirements, and I also learned about the supporting token possibilities when configuring the services.
I found a link about supporting tokens at MSDN, and by following this recipe I have implemented the endpoint on the server with the following custom binding (I switched to configuration through code. Not sure if this can be set up in web.config as well.)
private static Binding CreateMultiFactorAuthenticationBinding()
{
var httpsTransport = new HttpsTransportBindingElement();
// The message security binding element will be configured to require 2 tokens:
// 1) A username-password encrypted with the service token
// 2) A client certificate used to sign the message
// Create symmetric security binding element with encrypted username-password token.
// Symmetric key is encrypted with server certificate.
var messageSecurity = SecurityBindingElement.CreateUserNameForCertificateBindingElement();
messageSecurity.AllowInsecureTransport = false;
// Require client certificate as endorsing supporting token for all requests from client to server
var clientX509SupportingTokenParameters = new X509SecurityTokenParameters
{
InclusionMode =
SecurityTokenInclusionMode.AlwaysToRecipient
};
messageSecurity.EndpointSupportingTokenParameters.Endorsing.Add(clientX509SupportingTokenParameters);
return new CustomBinding(messageSecurity, httpsTransport);
}
This binding creates a SymmetricSecurityBindingElement where a symmetric key (encrypted with the server certificate) is used to encrypt a username/password security token in the message header, and the message body itself.
In addition a X509 security token is added as an endorsing, supporting token to the binding. This token is configured to always be included in the client requests to the server.
This custom binding was subsequently used to configure a new WCF-service with an endpoint requiring this binding. I am using the WcfFacility in Castle Windsor to configure the service.
This code does the following:
Sets the service certificate
Sets the validation mode for the client certificates to chain trust, so that incoming client certificates must be issued by a trusted root certificate authority in the server store
Adds custom validators for username/password credentials and client certificate
//// Registering WCF-services
var returnFaults = new ServiceDebugBehavior {IncludeExceptionDetailInFaults = true};
var metaData = new ServiceMetadataBehavior {HttpsGetEnabled = true};
var serviceCredentials = new ServiceCredentials();
// Configure service sertificate
serviceCredentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"ServerCertificate");
// Configure client certificate authentication mode
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.ChainTrust;
// Add custom username-password validator
serviceCredentials.UserNameAuthentication.UserNamePasswordValidationMode =
UserNamePasswordValidationMode.Custom;
serviceCredentials.UserNameAuthentication.CustomUserNamePasswordValidator =
_container.Resolve<MyServicesUsernameValidator>();
// Add custom certificate validator
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode =
X509CertificateValidationMode.Custom;
serviceCredentials.ClientCertificate.Authentication.CustomCertificateValidator =
_container.Resolve<MyServicesCertificateValidator>();
var serviceModel = new DefaultServiceModel();
serviceModel.AddEndpoints(
WcfEndpoint.ForContract<IMyContract>().BoundTo(CreateMultiFactorAuthenticationBinding()));
serviceModel.BaseAddresses.Add(new Uri("https://server.com/MyServiceImplementation.svc"));
serviceModel.AddExtensions(serviceCredentials);
serviceModel.AddExtensions(metaData);
_container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero)
.Register(Component.For<IMyContract>()
.ImplementedBy<MyServiceImplementation>()
.AsWcfService(serviceModel),
Component.For<IServiceBehavior>().Instance(returnFaults));
MyServicesUsernameValidator inherits UserNamePasswordValidator and MyServicesCertificateValidator inherits X509CertificateValidator. Both overrides their corresponding Validate methods.
This seems to solve my particular problem... Hope it solves yours! :)
That is not possible to define in configuration with out of the box bindings. Even custom binding doesn't support enough infrastructure to define such binding in configuration.
First you will definitely need two endpoints for this. One will be used for clients with user name / password only. This endpoint can be configured with some common binding expecting either Message security with UserName client credentials or transport security with message credentials. The second endpoint will be for your more complex validation. This endpoint needs new binding defined in code. This binding must use:
Asymetric security binding element (mutual certificate authentication)
X.509 security token as primary security token
User name security token as supporting security token
This is example of the binding I had to use when communicating with similar service:
Custom binding = new CustomBinding();
var userNameToken = new UserNameSecurityTokenParameters();
userNameToken.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
var securityElement = new AsymmetricSecurityBindingElement();
securityElement.IncludeTimestamp = true;
securityElement.RecipientTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.SubjectKeyIdentifier, SecurityTokenInclusionMode.Never);
securityElement.InitiatorTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.SubjectKeyIdentifier, SecurityTokenInclusionMode.AlwaysToRecipient);
securityElement.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256;
securityElement.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
securityElement.SetKeyDerivation(false);
securityElement.EndpointSupportingTokenParameters.SignedEncrypted.Add(userNameToken);
securityElement.MessageProtectionOrder = MessageProtectionOrder.EncryptBeforeSign;
securityElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11;
binding.Elements.Add(securityElement);
var encodingElement = new TextMessageEncodingBindingElement();
encodingElement.MessageVersion = MessageVersion.Soap12WSAddressingAugust2004;
binding.Elements.Add(encodingElement);
var httpElement = new HttpTransportBindingElement();
httpElement.UseDefaultWebProxy = true;
binding.Elements.Add(httpElement);
This example uses CustomBinding defined in code. If you want to use this in configuration you must create whole new binding and binding extension and register that extension in configuration file.
Even then I'm not sure that both validators will be used - I used this as the client of the service. The main point is that request can have only single main token and it is possible that default WCF infrastructure will choose only one to validate but such logic can be also replaced.
We currently support several WCF services running in a load balanced environment. In the past, we have used wsHttpBinding and set establishSecurityContext to false to allow the service to work properly with our load balancer.
An issue we have ran into is that the wsHttpBinding encrypts the return results by default and, apparently, cannot be turned off. This causes issues with the Riverbed compression appliance we have on our network - i.e. the encrypted data does not compress very well (or at all).
Now we're attempting to make use of the basicHttpBinding since it does not encrypt the data by default. We have two requirements:
Work with the load balancer - this appears to be possibly by using setting the keepAliveEnabled to false. This requires the use of a custom binding. For example:
<customBinding>
<binding name="NewBinding0">
<httpTransport authenticationScheme="Ntlm" **keepAliveEnabled="false"** />
</binding>
</customBinding>
Passes User Credentials - this appears to be possible by setting the security mode to TransportCredentialOnly. This is available with the basicHttpBinding. For example:
<basicHttpBinding>
<binding name="NewBinding1">
<security **mode="TransportCredentialOnly"** />
</binding>
</basicHttpBinding>
So now for my actual question :-)... How/Is it possible to combine the above two requirements into a single custom binding? What is the equivalent of #2 above for a custom binding? How can I get it to pass the user credentials?
Thanks!
It turns out I was able to do what I wanted with the custom binding using the following binding configuration:
<customBinding>
<binding name="NewBinding0">
<httpTransport authenticationScheme="Ntlm" keepAliveEnabled="false" />
</binding>
</customBinding>
Then, on the client side, I could use the following code to get the identity of the application pool the WCF service was running under (in IIS) as well as the identity of the user who actually called the WCF service:
public string GetData(int value)
{
var callingUser = string.Empty;
var appPoolUser = WindowsIdentity.GetCurrent().Name;
var identities =
OperationContext.Current.ServiceSecurityContext.AuthorizationContext.Properties["Identities"] as
IList<IIdentity>;
if (identities != null)
{
var result = from i in identities
where i.AuthenticationType == "NTLM"
select new { i.Name };
if (result.Count() > 0)
{
callingUser = result.First().Name;
}
}
return string.Format("Value Entered: {0}; AppPool User: {1}; Calling User: {2}", value,
appPoolUser, callingUser);
}
I tested the above code in a load balanced environment, for Requirement #1, and everything seemed to run just fine. The tests simulated a 100-user load for 10 minutes in a load balanced environment. We took one of the load balanced servers down during the test and everything continued to run as expected (i.e. no exceptions were thrown during the tests nor did any identities come back incorrectly).
The code above is the key piece, for Requirement #2, that I was missing - i.e. I didn't realize until this research that WCF would give you multiple identities.
Also, using this configuration, the results of the WCF call is not encrypted (which is what we wanted). So, I think this configuration will work for our situation just fine.
im trying to implement simple secured client server communiction using WCF.
when im launching mt server everty thing is OK , But when im launching my client im getting this error:
Error : An error occurred while making the HTTP request to https://localhost:800
0/ExchangeService. This could be due to the fact that the server certificate is
not configured properly with HTTP.SYS in the HTTPS case. This could also be caus
ed by a mismatch of the security binding between the client and the server.
this is the server code :
Uri address = new Uri("https://localhost:8000/ExchangeService");
WSHttpBinding binding = new WSHttpBinding();
//Set Binding Params
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
Type contract = typeof(ExchangeService.ServiceContract.ITradeService);
ServiceHost host = new ServiceHost(typeof(TradeService));
host.AddServiceEndpoint(contract, binding, address);
host.Open();
this is the client configuration (app.config):
</client>
<bindings>
<wsHttpBinding>
<binding name="TradeWsHttpBinding">
<security mode="Transport">
<transport clientCredentialType="None"
proxyCredentialType ="None"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
the security configuration at both the client and the server are the same , and i dont need certificate for the server in that kind of security (transport) so why do i get
this exception ????
thanks...
Well looking at your code:
Uri address = new Uri("https://localhost:8000/ExchangeService");
You're specifying that the address is using SSL (https) so it would require a certificate for that purpose. Either use a http binding or install a certificate.
I'd check out the Application Scenarios and How Tos section on this CodePlex Link for different configurations and details of how to configure them.
I've been given a web service written in Java that I'm not able to make any changes to. It requires the user authenticate with basic authentication to access any of the methods. The suggested way to interact with this service in .NET is by using Visual Studio 2005 with WSE 3.0 installed.
This is an issue, since the project is already using Visual Studio 2008 (targeting .NET 2.0). I could do it in VS2005, however I do not want to tie the project to VS2005 or do it by creating an assembly in VS2005 and including that in the VS2008 solution (which basically ties the project to 2005 anyway for any future changes to the assembly). I think that either of these options would make things complicated for new developers by forcing them to install WSE 3.0 and keep the project from being able to use 2008 and features in .NET 3.5 in the future... ie, I truly believe using WCF is the way to go.
I've been looking into using WCF for this, however I'm unsure how to get the WCF service to understand that it needs to send the authentication headers along with each request. I'm getting 401 errors when I attempt to do anything with the web service.
This is what my code looks like:
WebHttpBinding webBinding = new WebHttpBinding();
ChannelFactory<MyService> factory =
new ChannelFactory<MyService>(webBinding, new EndpointAddress("http://127.0.0.1:80/Service/Service/"));
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
factory.Credentials.UserName.UserName = "username";
factory.Credentials.UserName.Password = "password";
MyService proxy = factory.CreateChannel();
proxy.postSubmission(_postSubmission);
This will run and throw the following exception:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server
was 'Basic realm=realm'.
And this has an inner exception of:
The remote server returned an error: (401) Unauthorized.
Any thoughts about what might be causing this issue would be greatly appreciated.
First question: is this a SOAP or a REST based Java service you're trying to call?
Right now, with the "webHttpBinding", you're using a REST-based approach. If the Java service is a SOAP service, then you'd need to change your binding to be "basicHttpBinding" instead.
IF it's a SOAP based service, you should try this:
BasicHttpBinding binding = new BasicHttpBinding();
binding.SendTimeout = TimeSpan.FromSeconds(25);
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType =
HttpClientCredentialType.Basic;
EndpointAddress address = new EndpointAddress(your-url-here);
ChannelFactory<MyService> factory =
new ChannelFactory<MyService>(binding, address);
MyService proxy = factory.CreateChannel();
proxy.ClientCredentials.UserName.UserName = "username";
proxy.ClientCredentials.UserName.Password = "password";
I've used this with various web services and it works - most of the time.
If that doesn't work, you'll have to find out more about what that Java webservice expects and how to send that relevant info to it.
Marc
First of all put the following in your app.config or your web.config. (no need to change this as you move it through environments):
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IConfigService">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:55283/ConfigService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IConfigService"
contract="IConfigService" name="BasicHttpBinding_IService" />
</client>
</system.serviceModel>
Change the contract attribute to the the Namespace.Interface name accordingly.
Note the security mode = TransportCredentialOnly
Now to programmatically change the endpoint and pass the credentials, use the following code:
var myBinding = new BasicHttpBinding("BasicHttpBinding_IConfigService");
var myEndpoint = new EndpointAddress("http://yourbaseurl/configservice.svc");
var myChannelFactory = new ChannelFactory<IConfigService>(myBinding, myEndpoint);
var credentialBehaviour = myChannelFactory.Endpoint.Behaviors.Find<ClientCredentials>();
credentialBehaviour.UserName.UserName = #"username";
credentialBehaviour.UserName.Password = #"password";
IConfigService client = null;
try
{
client = myChannelFactory.CreateChannel();
var brands = client.YourServiceFunctionName();
((ICommunicationObject)client).Close();
}
catch (Exception ex)
{
if (client != null)
{
((ICommunicationObject)client).Abort();
}
}
I will add to this as well based on a similar problem I just experienced. I auto-generated the config / proxy with VS -- but the config it created didn't actually work.
Although it had security mode="Transport" set correctly, it didn't have clientCredentialType="Basic" set. I added to that my config and it still didn't work. Then I actually removed the message security that the tool created since the service I'm contacting is SSL + Basic only:
<message clientCredentialType="UserName" algorithmSuite="Default" />
Voila -- it worked.
I'm not sure why this had an effect considering the element did not specify message level security... but it did.
I have a WCF service that uses basicHttpbinding in development.
Now in product we want to use SSL, what changes do I have to make to force SSL connections only?
This page on MSDN explains WCF Binding Security.
http://msdn.microsoft.com/en-us/library/ms729700.aspx
The BasicHttpBinding class is
primarily used to interoperate with
existing Web services, and many of
those services are hosted by Internet
Information Services (IIS).
Consequently, the transport security
for this binding is designed for
seamless interoperation with IIS
sites. This is done by setting the
security mode to Transport and then
setting the client credential type.
The credential type values correspond
to IIS directory security mechanisms.
The following code shows the mode
being set and the credential type set
to Windows. You can use this
configuration when both client and
server are on the same Windows domain.
C#
BasicHttpBinding b = new BasicHttpBinding();
b.Security.Mode = BasicHttpSecurityMode.Transport ;
b.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
Or, in configuration:
<bindings>
<basicHttpBinding>
<binding name="SecurityByTransport">
<security mode="Transport">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
To enable ssl, without a login, set clientCredentialType to "None".
Options for security mode are:
None, Transport, Message, TransportWithMessageCredential and TransportCredentialOnly
You can find more details at: http://msdn.microsoft.com/en-us/library/system.servicemodel.basichttpsecuritymode.aspx
I just faced the same problem and found this MSDN article:
How to: Configure an IIS-hosted WCF service with SSL
At the end of the article you will find the xml configuration of the WebConfig file.
The solution worked just fine for me. One more thing to say, keep in mind that you need a REAL certificate for your release!
I think that if under your bindings where you have the <Security mode="Transport">, if you would change it to be <security mode="None">, you would be ok.
This is a copy of a code base that I'm working on and I tried that in-code, and it appears to be working.
I get the WSDL at least when I call the service, if that helps at all.
BasicHttpBinding basicBinding = new BasicHttpBinding();
if (RegistryConnectionStringFactory.UseSslForCommunications)
{
basicBinding.Security.Mode = BasicHttpSecurityMode.TransportWithMessageCredential;
basicBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
}
else
{
basicBinding.Security.Mode = BasicHttpSecurityMode.None;
basicBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
}