I have an application that connects via https to a SOAP-based web service that implements WS-Security. The web service is written in Java, expects a plain text password as well as a properly set timestamp.
After a great deal of googling and experimentation, I can't figure out how to configure my WCF client to interact with this service. In addition to a correct answer, I would also appreciate a link to a tutorial that explains WCF and SOAP well.
My current client's app.config looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="MyServiceSoapBinding" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
<!--security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security-->
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://p1.my.com/tx/services/MyService"
binding="basicHttpBinding" bindingConfiguration="MyServiceSoapBinding"
contract="My.IMyService" name="MyServiceEndpointPort" />
</client>
</system.serviceModel>
</configuration>
and the client code looks like this:
string response;
try
{
MyService.MyServiceClient svc = new WcfExample.MyService.MyServiceClient();
svc.ClientCredentials.UserName.UserName = "myUser";
svc.ClientCredentials.UserName.Password = "myPass";
response = svc.ping();
lblPingResponse.Text = response;
}
catch (System.ServiceModel.Security.MessageSecurityException mse)
{
lblPingResponse.Text = "MessageSecurityException: " + mse.Message;
}
catch (Exception ex)
{
lblPingResponse.Text = "Exception: " + ex.Message;
}
This code is throwing this exception:
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."
The WSE 3 version simply requires the following to work:
ServiceUsernameTokenManager.AddUser(userName, password);
UsernameToken token = new UsernameToken(userName, password,
PasswordOption.SendPlainText);
proxy = new _MyServiceWse();
Policy policy = new Policy();
policy.Assertions.Add(new UsernameOverTransportAssertion());
policy.Assertions.Add(new RequireActionHeaderAssertion());
proxy.SetPolicy(policy);
proxy.SetClientCredential(token);
UPDATE:
The request now reaches the server and a response is sent back from the server using this configuration in app.config:
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
The client then throws an Exception
"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."
This seems to be because the client sends a Timestamp header, but the service does not return a Timestamp header. This is probably "the right thing to do", but it's not very helpful because there are many web services deployed out there that expect a Timestamp but do not return one.
If there is a way to convince the client to accept this situation I would love to know about it. In the mean time, I'll look into whether I can have the web service changed to return a Timestamp.
Although I realise that you have already marked the answer from Mark_S as correct, you may find the following resolves your issue.
Your error message is addressed in Microsoft Hotfix KB971493: A hotfix that enables WCF to send secured messages and to receive unsecured responses, and to send unsecured messages and to receive secured responses, is available for the .NET Framework 3.5 SP1.
Windows Communication Foundation (WCF) does not have the functionality to send secured messages and then receive unsecured responses, or to send unsecured messages and receive secured responses. The hotfix that is described in this article adds a new enableUnsecuredResponse attribute.
I would be interested to know if this solves your problem.
As for your concrete problem, have a look at a few similar questions on Stackoverflow and elsewhere:
How to authenticate in WCF services in BasicHttpBinding?
http://icoder.wordpress.com/2007/06/22/how-to-setup-a-wcf-service-using-basic-http-bindings-with-ssl-transport-level-security/
Links to useful tutorials and screen casts explaining WCF in great detail: there's the MSDN WCF Developer Center which has everything from beginner's tutorials to articles and sample code.
Also, I would recommend you have a look at the Pluralsight screen casts on WCF - it's an excellent series going from "Creating your first WCF service" and "Creating your first WCF client" all the way to rather advanced topics. Aaron Skonnard very nicely explains everything in 10-15 minutes screencasts - highly recommended!
Related
I've successfully published a WCF service on our production server. I can navigate to the page and see the default WCF page.
I can add the service via "Add Service Reference" to a client on my computer. I can also "Update Service Reference" with no problems what so ever.
The service was confirmed to work on my local computer with and without the client by running the default debugger that comes with VS2010.
When trying to call a service method (from the server hosted service), I receive this exception..
There was no endpoint listening at that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
InnerException...
{"The remote server returned an error: (404) Not Found."}
I've also tried running a client on the server itself to see if I can connect but I get the same error.
I even tried just deploying the basic WCF service that's created for you when you create a WCF service library (very basic), changed nothing, deployed successfully, and get the same error.
What am I doing wrong? I've went down the road of IIS configurations, SSL certificates, etc but nothing seems to fix it. I feel as though I'm missing something very basic here.
EDIT CLIENT CONFIG...
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://SERVER_NAME.DOMAIN_NAME.com/TestService/TestService.Service1.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" />
</client>
</system.serviceModel>
</configuration>
Some pitfalls to check:
1) Check if the service URL is correct on your configuration files. This includes the protocol. It happened to me recently: the .config file had "http://" and the webserver was only accessible through HTTPS.
2) Disable the "required client certificate" setting on IIS.
3) If your service is inside an asp.net application, check if there are any restrictions of Authentication / Authorization in place. Allow anonymous access to your .SVC
Is it possible to create a WebService using JAX-WS, that whould then be consumed by a WCF client using such a binding?
<bindings>
<basicHttpBinding>
<binding name="CaseObjectServicePortBinding" >
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Certificate"/>
<message clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
I created such a service without WSIT for now, just a plain service and wanted to just ignore the "Security" header in incoming SOAP message. But it fails with:
"Could not establish secure channel for SSL/TLS with authority 'xxxxxxxxxx'."
If I change:
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Certificate"/>
<message clientCredentialType="Certificate" />
</security>
to:
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
<message clientCredentialType="Certificate" />
</security>
everything works flawlessly. Any ideas what am I doing wrong?
The answer is yes. You can use BasicHttpBinding or WsHttpBinding
The error was occurred because when you use TransportWithMessageCredential, the WCF client will impose additional security to your message sent through the wire, which is interoperable only to WCF service.
Once you changed it to Transport, only transport security( SSL using certificate) is applied , so that why both client and service can understand how to communicate with each other.
Cheers.
When defining security as TransportWithMessageCredential you say: I want a service which will communicate over secured transport channel (HTTPS) and I want to pass client credentials in SOAP header.
If you define Certificate credential type in message element you say: The SOAP header will transport client credentials as x.509 Certificate token profile. It is interoperable format which requires WS-Security on the service.
If you define Certifiate credential type in transport element you say: I want mutual SSL authentication. I'm actually not sure if this is used if you define TransportWithMessageCredential
This happened on the step of initiating the request; the TLS exception pops out to you because the certificate set on the client is not trusted. Use a certificate with the common destination name, if you are using the service on public use the domain name else use the destination IP address as a common name and it will work just fine .
PS: Use the 'basichttps' binding in case you want to proceed with the https content type 'text/xml' soap 11 the the default from jaxws
On a production server (Windows Server 2003 SP2) I can connect to a remote WCF service with Internet Explorer 8: When I browse to the URL http://www.domain.com/Service.svc (where my service listens) I get the expected info page of the service displayed. Connection settings in Internet Explorer only specify "auto detect", proxy settings are disabled.
If I start a console application (built with WCF in .NET 4.0) on the same server which also tries to connect to the same WCF service it fails telling me that no endpoint was available listening on http://www.domain.com/Service.svc.
Configuration of the WCF client:
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IMyService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false"
hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192"
maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="None">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://www.domain.com/Service.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IMyService"
contract="Service.IMyService" name="WSHttpBinding_IMyService" />
</client>
</system.serviceModel>
<configuration>
With these settings I can communicate successfully with the remote service from my development machine.
Looking around for other options I found that I can specify to use the Internet Explorer proxy settings with:
<system.net>
<defaultProxy>
<proxy usesystemdefault="true" />
</defaultProxy>
</system.net>
It didn't work and I am not sure if I understood this setting really correctly. (My hope was that the WCF client will adopt the "autodetect" setting of Internet Explorer and then connect the same way to the service like the installed IE.)
I also had toggled the useDefaultWebProxy setting in the binding configuration between true and false with no success.
Now I am asking for help what I can do? Which settings might be wrong or missing? What could I test and how can I get more detailed error messages to better identify the problem?
Thank you in advance!
Edit:
Stack in Innerexception is saying:
System.Net.WebException: Connection to remote server could not be established
System.Net.Sockets.SocketException: Connection failed since the host didn't answer after a certain time span or the connection was faulted since the connected host didn't answer.
Although Internet Explorer can connect to the service without specifying a proxy address but only enabling the "auto detect" feature this doesn't seem to work with my WCF client when setting <proxy usesystemdefault="true" />. (Documentation says: This will pickup the Internet Explorer settings. But it doesn't work.) Finally the customer gave me a concrete proxy address and I have changed the binding in my client configuration the following way:
Changed: useDefaultWebProxy="false" (instead of true)
Added: proxyAddress="http://10.20.30.40:8080" (Edit2: Not only IP-address! The prefix with http:// is important! Otherwise it will throw new exceptions, see the follow-up question below.)
With this the WebException and SocketConnection disappeared and the Client seems to connect to the Service but I am having now the next issue when calling the first service operation. I will put this in an new question.
Edit: Here is the follow-up question:
Strange exception when connecting to a WCF service via a proxy server
Edit2: According to the answer in the follow-up question it is important to prefix the proxyAddress with http. (changed my answer now)
Did you maybe just introduce a typo into your address on the client??
address="http://www.domain.com/Service.scv"
Shouldn't that be
address="http://www.domain.com/Service.svc"
(.svc instead of .scv at the end) (confirmed as no typo in reality)
Also, this address would indicate your *.svc file is in the root of that machine - is that really the case?? Normally in IIS, your address will be made up of machine name, virtual directory where the *.svc file resides, and the *.svc file itself, so something like:
http://www.domain.com/ServiceDirectory/Service.svc
I am not sure how you are hosting your service, IIS?
I didn't see anything wrong really in the configuration, other than
<security mode="None">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"/>
</security>
If you say that when you type in the address in the IE you see the service, then it is leading me to believe that it is security setting that are wrong. Try removing the security block form the client config file or where ever you have it and see if that works....
If it does, we might have it narrowed it down...
The issue here may have been as simple as the case of true vs True
<proxy usesystemdefault="True" />
In a Project being upgraded I have to Consume a WCF service ( Service A) published by 3rd party ( no Control) in my WCF Service.
I have been using ServiceA in My Web App project ( vs2008) and it has been working fine.
I started by Adding Service Reference in my WCF Project ( ServiceB). Lets say Name of the Service is "XYZ". VS created all required files but when I tried to compile it gave error
The type name 'XYZ' does not exist in the type 'ServiceB.ServiceB';
My 'Service B' has 'ServiceB.SVC'
I tried to overcome this by removing namespace "ServiceB." from Reference.cs file and Its content. This code could then be compiled.
Now I get Exception that
"The caller was not authenticated by the service."
Inner exception
The request for security token could not be satisfied because authentication failed.
at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
=>
((System.ServiceModel.FaultException)(ex.InnerException)).Message
The request for security token could not be satisfied because authentication failed.
The Web.Config File on ServiceB is as follows:
<wsHttpBinding>
<binding name="WSHttpBinding_IABCService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://aaaaa/ ServiceA.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IABCService"
contract="XYZ.IABCService" name="WSHttpBinding_IABCService">
<identity>
<servicePrincipalName value="host/[hostname]" />
</identity>
</endpoint>
</client>
======
I made my self believe that may be the problem is in WCF accessing WCF. I created a Web Service (.asmx) and added reference to ServiceA . When I debug by invoking the method I get the results from ServiceA . Hoping this was a solution that I can use till I figure out between WCF and WCF issue, I added reference to asmx service to My WCF Service ( ServiceB).
When I debug by Running ServiceB --> asmx --> Service A, I again get Authentication failed for user error!!!
I believe it has something to do with Identity being impersonated ...
I read that ServiceB's web.config taking Priority over asmx web.config but I was not able to get to a solution.
I cannot turn off " Security Mode=None" as then Service A responds by saying no tokens were passed.
Any help will be appreciated:
Remember that I am able to use the WC Service A from WebApp and from asmx but not from another WCF directly or Indirectly.
Thank you
Mar
This has nothing at all to do with WCF calling WCF. Try to imagine that WCF calling WCF causes compile errors - and that you're the first person in the world to discover this since 2006!
Try to fully qualify the "XYZ" type, spelling out the full namespace. If that doesn't work, then right-click each service reference and choose "View in Object Browser". Look to see what the full name of the types is.
If none of this helps you, then please post complete error messages and/or exceptions.
And, please don't ever edit Reference.cs. Any of your edits would be destroyed the next time that "Update Service Reference" is performed. Therefore, any change you think you want to make to Reference.cs can either be done in a better way without changing it, or else you really don't want to make that change at all (like the change you made).
It seem to me that you need to have a delegation instead of impersonation. To convert current users token to another one you can use DuplicateToken DuplicateTokenEx (the last one can produce TokenPrimary) with SecurityDelegation
SecurityImpersonation (SECURITY_IMPERSONATION_LEVEL) and then WindowsIdentity(IntPtr) constructor. See LogonUser and delegation as a example.
Another way is Protocol Transition: S4U, S4U2Self (see http://msdn.microsoft.com/en-us/magazine/cc163500.aspx, http://msdn.microsoft.com/en-us/library/ff650469.aspx, http://msdn.microsoft.com/en-us/library/ms998355.aspx). You can use something like new WindowsIdentity(clientUPN).Impersonate().
If you have to do a delegation (i.e. act as original caller) process is described in detail on this MSDN Forum thread. Don't use a full trust delegation -- it requires admin permissions for AD and it is not a good thing from security prospective. Constrained delegation is easy to setup correctly if you know which names to use in SETSPN command. Also make sure you run service B (the delegator) using domain user account, machine accounts like Network Service/Local Service or local user accounts won't work because you won't be able to create SPN for those.
Might seem like a silly question, but everything in WCF seems a lot more complicated than in asmx, how can I increase the timeout of an svc service?
Here is what I have so far:
<bindings>
<basicHttpBinding>
<binding name="IncreasedTimeout"
openTimeout="12:00:00"
receiveTimeout="12:00:00" closeTimeout="12:00:00"
sendTimeout="12:00:00">
</binding>
</basicHttpBinding>
</bindings>
And then my endpoint gets mapped like this:
<endpoint address=""
binding="basicHttpBinding" bindingConfiguration="IncreasedTimeout"
contract="ServiceLibrary.IDownloads">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
The exact error I am getting:
The request channel timed out while waiting for a reply after 00:00:59.9990000. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout.
In the WCF Test Client, there is a config icon that contains the run time configuration of my service:
As you can see its not the same values as I've set for it? What am I doing wrong?
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IDownloads" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="">
<extendedProtectionPolicy policyEnforcement="Never" />
</transport>
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
In your binding configuration, there are four timeout values you can tweak:
<bindings>
<basicHttpBinding>
<binding name="IncreasedTimeout"
sendTimeout="00:25:00">
</binding>
</basicHttpBinding>
The most important is the sendTimeout, which says how long the client will wait for a response from your WCF service. You can specify hours:minutes:seconds in your settings - in my sample, I set the timeout to 25 minutes.
The openTimeout as the name implies is the amount of time you're willing to wait when you open the connection to your WCF service. Similarly, the closeTimeout is the amount of time when you close the connection (dispose the client proxy) that you'll wait before an exception is thrown.
The receiveTimeout is a bit like a mirror for the sendTimeout - while the send timeout is the amount of time you'll wait for a response from the server, the receiveTimeout is the amount of time you'll give you client to receive and process the response from the server.
In case you're send back and forth "normal" messages, both can be pretty short - especially the receiveTimeout, since receiving a SOAP message, decrypting, checking and deserializing it should take almost no time. The story is different with streaming - in that case, you might need more time on the client to actually complete the "download" of the stream you get back from the server.
There's also openTimeout, receiveTimeout, and closeTimeout. The MSDN docs on binding gives you more information on what these are for.
To get a serious grip on all the intricasies of WCF, I would strongly recommend you purchase the "Learning WCF" book by Michele Leroux Bustamante:
Learning WCF http://ecx.images-amazon.com/images/I/51GNuqUJq%2BL._BO2,204,203,200_PIsitb-sticker-arrow-click,TopRight,35,-76_AA240_SH20_OU01_.jpg
and you also spend some time watching her 15-part "WCF Top to Bottom" screencast series - highly recommended!
For more advanced topics I recommend that you check out Juwal Lowy's Programming WCF Services book.
Programming WCF http://ecx.images-amazon.com/images/I/41odWcLoGAL._BO2,204,203,200_PIsitb-sticker-arrow-click,TopRight,35,-76_AA240_SH20_OU01_.jpg
The best way is to change any setting you want in your code.
Check out the below example:
using(WCFServiceClient client = new WCFServiceClient ())
{
client.Endpoint.Binding.SendTimeout = new TimeSpan(0, 1, 30);
}
The timeout configuration needs to be set at the client level, so the configuration I was setting in the web.config had no effect, the WCF test tool has its own configuration and there is where you need to set the timeout.
Got the same error recently but was able to fixed it by ensuring to close every wcf client call.
eg.
WCFServiceClient client = new WCFServiceClient ();
//More codes here
// Always close the client.
client.Close();
or
using(WCFServiceClient client = new WCFServiceClient ())
{
//More codes here
}