Enabling SSL for my WCF application is having problems - c#

we have developed a WCF webservice and it has been working fine without any requirement of SSL being introduced. Now, before deploying it to Test envirnment we need to make it SSL enabled.
So I came across this link on SO : Enable SSL for my WCF service
and changed my config file to include following code:
<binding name="BasicHttpBinding_IPromotionalSponsorship" allowCookies="true"
maxReceivedMessageSize="20000000"
maxBufferSize="20000000"
maxBufferPoolSize="20000000">
<readerQuotas maxDepth="32"
maxArrayLength="200000000"
maxStringContentLength="200000000"/>
<security mode="Transport">
</security>
</binding>
While my endpoint looks like following
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IPromotionalSponsorship"
name="StandardEndpoint" contract="PL.Commercial.PromoSponsor.Service.Contracts.IPromotionalSponsorship" />
But it does not sound like it changed anything still when I run my project, it shows the URL without https.
Now, when i explicitly add 's' in the URl it throws following error:
Unable to make a secure connection to the server. This may be a
problem with the server, or it may be requiring a client
authentication certificate that you don't have.
I have created a self-signed certificate and added it to my local IIS. Anything special that I need to change/add?

You need to do following changes.
If you don't need client authentication and just HTTPS then your binding configuration should look like below. Note transport element.
<binding name="BasicHttpBinding_IPromotionalSponsorship" allowCookies="true"
maxReceivedMessageSize="20000000"
maxBufferSize="20000000"
maxBufferPoolSize="20000000">
<readerQuotas maxDepth="32"
maxArrayLength="200000000"
maxStringContentLength="200000000"/>
<security mode="Transport">
<transport clientCredentialType="None"/>
</security>
</binding>
If you want browser to display https when you browse service. Your service behaviour should look like below.
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpsGetEnabled="True" httpsGetBinding="mexHttpsBinding"/>
</behavior>
</serviceBehaviors>
</behaviors>
If you are getting error "Unable to make a secure connection to the server..." as mentioned in question that means you have configured service in such way that it requires client certificate. I guess you just need to enable HTTPS. In that case you don't need to configure client certificate requirement in IIS.
Your application's SSL settings should look like this.

Related

Not able to communicate with WCF service running on separate computer using net.tcp

I am not able to communicate with another computer that is in my local area network over a net.tcp connection unless I turn the security mode off. If I set the security mode to "None", communication works fine. With the service model configuration set up as below on both ends of the wire, I get the error message "The server has rejected the client credentials".
The WCF service is being hosted in a Windows service on the remote computer. From what I have seen in various documentation, my configuration is set up correctly. Any ideas on what I might be doing wrong? Wild suggestions welcomed.
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_ITcpService">
<security mode="Transport">
<transport clientCredentialType="Windows" />
</security>
<!--Communication works if I do this
<security mode="None" />
-->
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://199.198.3.5:8520/TcpService"
binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_ITcpService"
contract="TcpServiceReference.ITcpService"
name="NetTcpBinding_ITcpService">
</endpoint>
</client>
</system.serviceModel>

How to configure WCF service deployed on IIS and remote client to authenticate from remote client PC?

I'm a noob; please help me understand this authentication config / bindings stuff that confuses me so much.
I have a C# WCF service deployed on IIS 7 on Win 2008. My client is a Windows Forms C# app. My client works just fine when it's running from the same server where the WCF service is running, but when I try to run my client from a remote PC, I get the following exception...
System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service.
I've read a few posts about these issues, and know that my problem is because my service and client are configured to use Windows authentication, which I guess is the default when using Visual Studio to create the service, and to add the service reference to the client. Below is my config before I made any changes, when it was still set to Windows (with irrelevant bits removed)...
Web.Config
<system.web>
...
<authentication mode="Windows"/>
...
<system.serviceModel>
<services>
<service name="MCLaborServer.LaborService" behaviorConfiguration="MCLaborServer.LaborServiceBehavior">
<!-- Service Endpoints -->
<endpoint address="" binding="wsHttpBinding" contract="MCLaborServer.ILaborService">
<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
-->
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MCLaborServer.LaborServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
And from the App.Config on the client...
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ILaborService" 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://<myDnsNameGoesHere>/MCLaborServer/LaborService.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ILaborService"
contract="LaborService.ILaborService" name="WSHttpBinding_ILaborService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
So, first I changed "authentication mode="None"" in the web.config, and set "security mode="None"" in the client's app.config, and set both the clientCredentialType="None" for message and transport. I also commented out the "identity" sections in both the web.config and client's app.config. That broke it completely though and now the client running locally won't even work; it gives a "The remote server returned an unexpected response: (405) Method Not Allowed" error.
So what can I do to turn security off so that I can connect using a remote client? I do have anonymous access enabled by the way in IIS for my application.
I'd also like to ask, what's the best practice way to configure this so I can make webservice calls on a remote client over the internet in a semi-secure fashion without using SSL or doing anything that would cost money. I'm not really that concerned about security of the data because it's not really sensitive data, but still I'd like to make sure the server isn't open to attacks.
Also, I read that I can use Windows authentication, and then explicitly specify credentials in code, like below. If I do that, will it still work remotely? And if so, does that end up making my Windows credentials for the server be sent over the wire in an insecure fashion, so then I'd be open to getting my credentials hijacked?
SomeService.ServiceClient someService = new SomeService.ServiceClient();
someService.ClientCredentials.Windows.ClientCredential.UserName="windowsuseraccountname"
someService.ClientCredentials.Windows.ClientCredential.Password="windowsuseraccountpassword"
I've read through the following posts / links, but still am confused. Thanks for any help!
WCF error: The caller was not authenticated by the service
How to fix "The caller was not authenticated by the service"?
http://msdn.microsoft.com/en-us/library/aa291347(v=vs.71).aspx
http://www.devx.com/codemag/Article/33342/1763/page/2
We ran into similar issues when setting up low security WCF services that ran across domains. One of the biggest problems (if you can call it that) is that WCF is configured to be very secure by default. Because our application was entirely within a secure network, we did not want to have to bother with a lot of complicated certificates. Our workaround was to create a custom binding that allowed us to use username/password authentication for our services without any encryption. We based our implementation off of Yaron Naveh's Clear Username Binding. I would recommend that you have a look at that (and at his blog post introducing it).
Some good resources for learning about WCF Bindings and Security:
MSDN - Windows Communication Foundation Bindings Overview
MSDN - System-Provided Bindings
MSDN - Security Overview
MSDN - Programming WCF Security
MSDN - WCF Security Fundamentals
MSDN - Choosing a Transport
I fixed this by changing binding to basicHttpBinding, changing authentication to Forms and turning security off.

call lists.asmx getting 'http request is unauthorized with client authentication scheme 'ntlm'

Using console app in C# to call lists.asmx getting 'http request is unauthorized with clien tauthentication scheme 'ntlm'. The authentication header received from the server was 'Negotiate, NTLM'.
Environment:
Kerberos turned on in QA & Production, not in Dev (stupid I know, but I don't admin any of the boxes)
Hitting a sharepoint webservice to GET data from a sharepoint list (lists.asmx).
Server uses ssl.
I get an error message in my qa environment as follows (can't paste the stacktrace as it's in a picture only):
System.ServiceModel.Security.MessageSecurityException: The HTTP request is unauthorized with client authentication scheme 'Ntlm'. The authentication header received from the server was 'Negotiate,NTLM'. ---> System.Net.WebException: The remote server returned an error: (401) Unauthorized.
Direct navigation to the list works fine from every machine.
Code works in a development environment (on the server) which does not have kerberos enabled (should be, but isn't. I CANNOT change this).
Code works against production from a desktop machine which does have kerberos enabled
Code does not work in a QA environment which does have kerberos enabled. This is where I get the error
To call the webservice I do this (no other security-related code involved)
XmlElement element = this.LIstsServiceClient.GetListItems(listName, '', query, fields, '300', null, null);
My app.config is as follows
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="clientEndpointBehavior">
<clientCredentials>
<windows allowedImpersonationLevel="Delegation"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="ListsSoap" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="999999999" maxBufferPoolSize="524288" maxReceivedMessageSize="999999999"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="999999" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="Ntlm" proxyCredentialType="Ntlm" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
<client>
<endpoint address="https://servername/sitecollectionname/_vti_bin/Lists.asmx"
binding="basicHttpBinding" bindingConfiguration="ListsSoap"
contract="ListsService.ListsSoap" name="ListsSoap" behaviorConfiguration="clientEndpointBehavior" >
<identity>
<servicePrincipalName value="spn" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
Have a look here
Enabled Anonymous access (username and password of domain user)
Enabled Integrated Windows authentication
Or, as lextm-MSFT says, check you are passing a valid set of user credentials
I resolved problem :
putting this is Config
<system.serviceModel>
<bindings />
<client />
</system.serviceModel>
It is simply an authentication failure. Check if your console application sends a valid user credential to IIS that hosts this web service.
I never did manage to find the answer to this, but mostly because I did not have access to consistently configured environments, hence I was unable to debug my code. I believe the issue to be a configuration problem, probably Kerberos related.
I solved this by allowing impersonation on the client endpoint, which the Lists service seems to require for the first request it receives (possibly depending on which web method you're calling). The Lists service will, confusingly, perform authentication and validation internally and if it fails, generate 401, or 500 responses which make it seem like your request is failing in IIS before hitting the service when in fact, the service method is executing and returning errors.
<behaviors>
<endpointBehaviors>
<behavior name="SPServiceBehavior">
<clientCredentials>
<windows allowedImpersonationLevel="Impersonation" allowNtlm="True"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
See my question here for all the details.

Keep MEX binding insecure while securing HTTP binding w/ UsernamePasswordValidator?

We're using UsernamePasswordValidator along with a certificate to secure access to our WCF services.
However, the custom authorization policies we're using are SERVICE behaviors, not endpoint behaviors, so they apply to all endpoints, including the MEX endpoint. We'd like to be able to go and grab the service references using visual studio without having to comment out the service behaviors every time, but since both the mex and the wshttp endpoint are secured, we get an error when doing "Add Service Reference.."
Is there any way around this?
Are you using the same binding on both? If so, try 2 seperate bindings - one for the mex endpoint and one for the wshttp:
So for the service - something like:
<wsHttpBinding><binding name="wsHttpBindingMessageUname">
<security mode="Message">
<message clientCredentialType="UserName" negotiateServiceCredential="true"
establishSecurityContext="false" />
</security></binding></wsHttpBinding>
and for the mex endpoint (no security):
<customBinding><binding name="customMex">
<textMessageEncoding>
<readerQuotas maxDepth="2147483647"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647" />
</textMessageEncoding>
<httpTransport transferMode="Buffered"
maxReceivedMessageSize="2147483647"
maxBufferSize="2147483647"/></binding></customBinding>
Service endpoints will be something like:
<endpoint address="" behaviorConfiguration="Server.Services.DefaultEndpointBehavior" binding="wsHttpBinding" bindingConfiguration="wsHttpBindingMessageUname" name="DefaultHttp" contract="Server.Services.IMyService" listenUriMode="Explicit" />
<endpoint address="mex" binding="customBinding" contract="IMetadataExchange" name="" bindingConfiguration="customMex" listenUriMode="Explicit" />
With this setup, it's not applying the security for mex so you shouldn't get that message when trying to update service reference. Either that, or create another secure binding that uses different credentials, i.e. a client certificate on your machine.
The following MSDN post has a sample of this and more info can be found on this blog regarding secure mex endpoints.
I think from the question he also noted that he was using Service Behaviours, so the binding configuration wont make a difference, since the entire service uses the UserNamePassword Validator.
Two things come to mind here.
Remove the explicit mex binding and add under service behaviors
<serviceMetadata httpsGetEnabled="true" />
Or Keep the mex binding, and enable
<serviceMetadata httpGetEnabled="true" />
CustomUserNameValidator doesnt get executed when requesting Metadata,
so if httpsgetenabled isnt on, and you have a mex binding on http, you need
httpGetenabled on at least

WCF sessions with HTTPS

I cannot figure out how to enable per-session instances for my WCF service while using HTTPS. (I'm not an ASP.NET expert but don't want to use ASP.NET session state if possible.) I am using .NET Framework 3.0.
I have arrived at the following contradiction and am hoping that someone can tell me where there is a flaw in the logic.
1) The service must be hosted on IIS 6 due to client mandate.
2) The service needs to maintain state between calls, including SqlConnection and SqlTransaction instances (ugly but necessary due to project constraints).
3) Therefore I need to use the wsHttpBinding.
4) The service needs to be able to access user authentication info from HttpContext.Current.User.Identity (e.g. using Windows security in IIS).
5) HTTPS is therefore required.
6) Transport-level security must therefore be configured on the binding.
7) Configuring the service to require sessions means I have to configure the wsHttpBinding to use Reliable Sessions.
8) This requires that message-level security is configured on the binding.
I.e. (6) and (8) are mutually exclusive.
It seems that using WCF sessions requires that I use message-level security, which prevents me from using HTTPS.
What am I missing?
3) True, wsHttpBinding and wsDualHttpBinding are the only HTTP bindings that support sessions
5) False, in order to authenticate the service callers you don't necessarily need to have any transport-level security (such as SSL/HTTPS). The only requirement is to configure IIS to enable Integrated Windows Authentication for a virtual directory. Then in WCF you have three possibilities to enable this scenario:
a) Use transport-level security on the wsHttpBinding with Windows credentials (HTTPS)
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="SecurityEnabledWsHttp">
<security mode="Transport">
<transport clientCredentialType="Windows" />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
b) Use message-level security on the wsHttpBinding with Windows credentials (HTTP)
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="SecurityEnabledWsHttp">
<security mode="Message">
<message clientCredentialType="Windows" />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
c) Run your service under the ASP.NET Compatibility Mode and enable Windows Authentication in ASP.NET (HTTP)
<system.web>
<authentication mode="Windows" />
</system.web>
Note that in a and b you will access the identity of the caller from within a service this way:
OperationContext.Current.ServiceSecurityContext.WindowsIdentity
6) True, transport-level security must be enabled on the wsHttpBinding in order to use HTTPS
7) False, Reliable Sessions is a particular implementation of Reliable Messaging for WCF sessions. Reliable Messaging is a WS-* standard specification designed to guarantee message delivery on an unreliable network. You can use WCF sessions without Reliable Messaging, and viceversa. Sessions are enabled on the service contract with this attribute:
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IMyService {
// ...
}
Also remember that in order to maintain state between service calls you will explicitly have to enable the appropriate instance mode on the service contract implementation:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class MyService : IMyService {
// ...
}
There are two kinds of sessions in WCF: Secure Sessions and Reliable Sessions. The default setting for both wsHttpBinding and netTcpBinding is to use Secure Sessions.For wsHttpBinding this is accomplished with message-level security by using the client's credentials, which is the default setting for the binding.For netTcpBinding instead, the session is established at the tranport level by using the facilities of the TCP protocol.
This means that simply switching to wsHttpBinding or netTcpBinding will enable support for WCF sessions.
The alternative is to use Reliable Sessions. This has to explicitly be enabled in the binding configuration, and removes the requirement of using message security for the wsHttpBinding. So this will work:
<bindings>
<wshttpbinding>
<binding name="ReliableSessionEnabled">
<reliablesession enabled="True" ordered="False" />
<security mode="None" />
</binding>
</wshttpbinding>
</bindings>
8) False, Reliable Sessions are used independently of the security settings of the communication channel.
For a more detailed explanation, have a look at this article.
Following through on Enrico's excellent answer, these are the configs I am using:
Service:
<services>
<service name="Foo.Bar.Service">
<endpoint name="EndpointHttps"
address=""
binding="customBinding" bindingConfiguration="EndpointHttps"
contract="Foo.Bar.IService" />
</service>
</services>
<bindings>
<customBinding>
<binding name="EndpointHttps">
<reliableSession />
<mtomMessageEncoding />
<httpsTransport />
</binding>
</customBinding>
</bindings>
Client:
<client>
<endpoint name="EndpointHttps"
address="https://server/FooBar/service.svc"
binding="customBinding" bindingConfiguration="EndpointHttps"
contract="Foo.Bar.IService" />
</client>
<bindings>
<customBinding>
<binding name="EndpointHttps">
<reliableSession />
<mtomMessageEncoding />
<httpsTransport />
</binding>
</customBinding>
</bindings>
Note: still haven't gotten this to work with Windows authentication though.

Categories