WCF net.tcp SSL, certificate and username + password authentication - wcf

I want to build a client-server, WPF-WCF application that should fulfill the following requirements:
Fast
Secured communication between clients and server
If an user wants to use the client application, he should have a certain certificate installed on his machine and also provide a valid username/password pair
Everything should happen over the internet
So I started working on it two days ago and, after going through almost every example/tutorial I could find that got close to my scenario, I managed to build a WCF service with net.tcp binding that is hosted in IIS (8 I think) and exposes its metadata through a mex endpoint and a tiny little client console application that can connect to the service and call its one and only HelloWorld method.
All was well until I started trying to add certificate based security. I tried countless configuration combinations and techniques but still couldn't get anything working.
At first, I got some specific error messages telling me various things about the server or client certificates not being valid, trusted, or good for anything in any way.
Then I followed these articles, since I need self-signed certificates while developing.
http://msdn.microsoft.com/en-us/library/ff647171.aspx
http://msdn.microsoft.com/en-us/library/ms733813(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/ff648498.aspx
Then, I started getting more and more vague error messages until I gave up.
It might very well be that I misunderstood how WCF works since I don't have that much experience with it.
The configuration that worked is this:
Service configuration
<configuration>
<system.web>
<httpRuntime targetFramework="4.5.1"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="TcpServiceBehaviour">
<serviceMetadata />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="TcpServiceBehaviour" name="WcfTcpServer.TcpService">
<endpoint address="net.tcp://serverName/wcftcpserver/TcpService.svc" binding="netTcpBinding" name="TcpServiceEndpoint" contract="WcfTcpServer.ITcpService" />
<endpoint address="SME" binding="mexTcpBinding" bindingConfiguration="" name="ServiceMetadataEndpoint" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
</configuration>
Client configuration
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
</startup>
<system.serviceModel>
<client>
<endpoint address="net.tcp://serverName/wcftcpserver/TcpService.svc" binding="netTcpBinding" contract="WcfTcpServer.ITcpService" name="TcpServiceEndpoint">
<identity>
<servicePrincipalName value="host/serverName.smth.smthElse.ro" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
That's, as you can see, the certificate-less configuration.
Any step that I take towards using my self-signed certificates within this configuration, breaks the applications.
I use .NetFramework 4.5.1 for everything.
I would very much appreciate help with this problem. I can provide additional configurations I tried, if needed.
Is this even possible or am I trying in vain?
Thank you!

In short, yes, you can support multiple client credentials using what are known as Supporting Tokens.
From the linked article:
The example adds an X.509 binary security token in addition to a
username security token. The token is passed in a WS-Security message
header from the client to the service and part of the message is
signed with the private key associated with the X.509 security token
to prove the possession of the X.509 certificate to the receiver. This
is useful in the case when there is a requirement to have multiple
claims associated with a message to authenticate or authorize the
sender.
On the topic of using NetTcpBinding over the internet:
NetTcpBinding is generally recommended for intranet scenarios. It is advised by much of what I have read to either use WsHttpBinding and BasicHttpBinding for internet scenarios depending on your requirements. If security is a top concern of yours - the recommended choice is WsHttpBinding with Message level security.
Guidelines on choosing a binding for an internet scenario: Internet Binding Scenarios.

Related

How to enable Windows Authentication and NetTCPBinding WCF webservice on IIS7?

I am attempting to set up a web service that uses windows authentication and NetTCPBinding on IIS 7. currently I am getting this error when I attempt to access the wysdl
"Security settings for this service require 'Anonymous' Authentication but it is not enabled for the IIS application that hosts this service. "
The relevant sections of my Config file look like this...
<behaviors>
...
<serviceBehaviors>
<behavior name="WCFHostService.MyServiceBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="UPMC.ISD.EADIS.ACO.ACOServiceConcept">
<endpoint name ="TCP_Binding"
address=""
binding="netTcpBinding"
contract="UPMC.ISD.EADIS.ACO.ACOServiceConcept.IACOService"/>
<endpoint name="mexHttpbinding"
contract="IMetadataExchange"
binding="mexTcpBinding"
address="mex" />
</service>
</services>
How do I get the "security settings" for my service to align to allow me to access this service? I also just read that you can get rid of the error by getting rid of your mex binding endpoints or by enabling anonymous authentication. But how do you get your wsdl if you don't have mex binding? Well I guess I will give that a go, but if you have any other advice I will most certainly take it.
Thanks.
According to WCF NetTcpBinding Security - how does it work?, the default security setting for NetTCP is Windows Authentication. It sounds like one or two things might be the issue:
Ensure Windows Authentication is enabled (in the IIS Management Console under Authentication - same place where you find the switch for Anonymous Authentication as in #Joel C's answer).
You might want to try specifying the windows account credentials when you create the client proxy, in case the account running the client is unable to authenticate.
Are the client and the server in the same domain?
Have you verified that Anonymous Authentication is enabled in the IIS application where you're hosting your service? In the IIS management console, browse to the site and application where your service is being hosted. Then make sure you have the "Features View" selected, and select the "Authentication" option. You should see various forms of authentication (anonymous, Windows, ASP.NET Impersonation, etc.) and each should say either enabled or disabled next to it.

Securing WCF hosted inside public MVC2 App

I've got a WCF service that is to be called by an application hosted on the web server (for the short-medium term, we'll only need a single web server so disregard scalability issues)
The web server serves a public website. at example.com
The WCF service exposes calls which amongst other things run jobs and provide certain admin functionality not supported by the web model eg long running database operations.
The WCF service has to be hosted inside the web site as it uses compatibility mode to take advantage of the Asp.Net http(s) pipeline - specifically, the service can generate emails and the emails are templated using MVC. One side-effect of this is that the call has to use the publicly visible hostname eg https://example.com/JobService.svc so that links in emails point to example.com as opposed to localhost or similar.
Obviously, I don't want the general public to be able to kick off jobs/admin tasks so I want to secure the WCF service.
I can only use https as opposed to net.tcp or similar for the binding thanks to relying on the Asp.net http pipeline.
I have to bind to the publicly accessible IP address to be able to use the proper hostname (unless someone knows a way around this?)
I can't use kerberos/NTLM as the server isn't on a domain (and NTLM is weak anyway)
I can't use certificates as it complains:
The SSL settings for the service 'SslRequireCert' does not match those of the IIS 'None'.
NB: I don't quite understand this as the website itself is only served via https. http simply returns a redirect to the same page via https.
(An interesting issue I'm having is that although the mex is served via https, the URLs inside the WSDL use http. I'm assuming this is a side-effect of not being able to set up TLS properly on my service so it thinks it's http even though it also responds on https)
So, I'm running out of ideas for how to secure my service. I could, of course, from within the service itself examine the request and determine if it comes from an IP used by the current server - but this feels very nasty and I'm effectively ignoring the work of experts and trying to put something in its place - Not a very good place to start.
Can anyone suggest a way to limit access to this service to processes on the local machine?
I've attached my current config below. (This is currently giving me the certificate error mentioned above)
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="WebJobServiceHTTPBinding" openTimeout="00:10:00"
sendTimeout="00:10:00">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"
aspNetCompatibilityEnabled="true">
<serviceActivations>
<add relativeAddress="WebJob.svc"
service="MyApp.WebJobService"
factory="MyApp.WCFDIServiceHostFactory" />
</serviceActivations>
</serviceHostingEnvironment>
<services>
<service behaviorConfiguration="WebJobServiceBehavior" name="MyApp.WebJobService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="WebJobServiceHTTPBinding"
name="HTTPEndpoint" contract="MyApp.JobService.Common.IWebJobService" />
</service>
</services>
<standardEndpoints>
<mexEndpoint>
<standardEndpoint name="WebJobServiceMex" />
</mexEndpoint>
</standardEndpoints>
<behaviors>
<serviceBehaviors>
<behavior name="WebJobServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<serviceCertificate findValue="[Thumbprint of x509 cert used by website for SSL]"
storeName="Root" x509FindType="FindByThumbprint" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
"Can anyone suggest a way to limit access to this service to processes on the local machine?"
Run your service in a different web site in IIS, if you're not already.
You could bind your service in IIS to the internal network IP address which would allow internal LAN clients to access the service but not external clients.
Another binding option is to bind to a port that is not open on your firewall in order to allow access from internal clients only. Even better, bind to a port that is not open on your firewall, and bind to the internal LAN IP.
You could also try binding to IP address 127.0.0.1.
In the end, I was forced to implement my own Authentication system. This was relatively simple as authenticatio implied authorization - ie no permission levels. That said, I'm still unhappy at the solution and will change it if another option presents itself.

WCF endpoints, baseAddressPrefixFilters, host headers

I have two websites on the same machine. The first (client) references a WCF service on the second site(server).
How do I set the address for the service reference? When moving from development on my local machine to the group development server, how do I change the url for the service? The sites are differentiated by host headers, like
http://dev.admin/...
and
http://dev.public/...
I sense that this can be handled using multiple endpoints, but I'm very new to WCF and really don't have a clue what I'm doing here.
After much frustration, I managed to determine that both web.config files (on the client and server, both of which are web apps in this case), the following sections have to be changed:
Client:
<client>
<endpoint
address="http://mysite.com:port/services/someservice.svc"
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_ISomeService"
contract="MyServices.ISomeService"
name="BasicHttpBinding_ISomeService" />
</client>
</system.serviceModel>
Server
<system.serviceModel>
<serviceHostingEnvironment>
<baseAddressPrefixFilters>
<add prefix="http://mysite.com:port/services"/>
</baseAddressPrefixFilters>
</serviceHostingEnvironment>
<behaviors>
<serviceBehaviors>
<behavior name="MyServices.SomeServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MyServices.SomeServiceBehavior"
name="MyServices.SomeService">
<endpoint address="http://mysite.com:port/services/someservice.svc"
name="endpoint.SomeService"
binding="basicHttpBinding"
bindingConfiguration=""
contract="MyServices.ISomeService"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
The thing to note here is that the host address in all three relevant sections (client endpoint address, server baseAddressPrefixFilter value, and server endpoint address) have to match.
I'm able to switch between servers by modifying these, as long as they match. I would still prefer a way to set this based on which machine the server is running on, but this works for the moment.
WCF Impressions
What's hot: persistent object. The client proxy object (created when you add a service reference) maintains a persistent connection to the service on the server. The service instance referenced by the client proxy maintains its state between calls, which can simplify method signatures and makes the client proxy object, and the service as a whole, much more useful for certain applications. Parameter object types can be shared between the client and server if they're declared in a common library, meaning that you don't have to create two very similar classes or wrapper classes to pass non-primitive data structures back and forth.
What's not: configuration is a royal pain, poorly documented, and far too involved. Getting this to work in a test/dev/staging/production environment configuration where the service neesd to be aware of its location is frustrating. I'm not convinced that making the service aware of its domain url (rather than, say, a relative path to whatever it's running on) has any significant benefit, security concerns aside.
That said, I'm continuing to go down the WCF path as the advantages thus-far outweigh the headaches.
Easiest way: run the WCF parts on different ports.

How to configure a WCF service to only accept a single client identified by a x509 certificate

I have a WCF client/service app that relies on secure communication between two machines and I want to use use x509 certificates installed in the certificate store to identify the server and client to each other. I do this by configuring the binding as <security authenticationMode="MutualCertificate"/>. There is only client machine.
The server has a certificate issued to server.mydomain.com installed in the Local Computer/Personal store and the client has a certificate issued to client.mydomain.com installed in the same place. In addition to this the server has the client's public certificate in Local Computer/Trusted People and the client has the server's public certificate in Local Computer/Trusted People.
Finally the client has been configured to check the server's certificate. I did this using the system.servicemodel/behaviors/endpointBehaviors/clientCredentials/serviceCertificate/defaultCertificate element in the config file.
So far so good, this all works. My problem is that I want to specify in the server's config file that only clients that identify themselves with the client.mydomain.com certificate from the Trusted People certificate store are allowed to connect.
The correct information is available on the server using the ServiceSecurityContext, but I am looking for a way to specify in app.config that WCF should do this check instead of my having to check the security context from code.
Is that possible? Any hints would be appreciated.
By the way, my server's config file looks like this so far:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="MyServer.Server" behaviorConfiguration="CertificateBehavior">
<endpoint contract="Contracts.IMyService" binding="customBinding" bindingConfiguration="SecureConfig">
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost/SecureWcf"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="CertificateBehavior">
<serviceCredentials>
<serviceCertificate storeLocation="LocalMachine" x509FindType="FindBySubjectName" findValue="server.mydomain.com"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="SecureConfig">
<security authenticationMode="MutualCertificate"/>
<httpTransport/>
</binding>
</customBinding>
</bindings>
</system.serviceModel>
</configuration>
There doesn't appear to be a way to do what I want using web.config.
I ended up adding a behavior with this tag:
<clientCertificate>
<authentication certificateValidationMode="PeerTrust" trustedStoreLocation="CurrentUser" revocationMode="NoCheck"/>
</clientCertificate>
And then add the client's certificate to the "trusted people" certificate store of the user that the server runs as.
Check out the WCF Security Guidance page on Codeplex - excellent and very useful stuff!
In particular, check out the How-To's and even more specifically the
How To – Use Certificate Authentication and Message Security in WCF calling from Windows Forms
It explains in great detail how to set up a WCF service which requires its clients to present a valid certificate, and how to check that. If you want to allow only a single client, deploy that certificate only specifically to that one single client.
Hope this helps!

WCF Security and what to use in this situation

I have a client/server application that has many client machines and one service on a server.....
On the server side I will be using a Windows Service to host my WCF service. The service will be passing data across the internet to the client machines. I figure I will be using wsHttpBinding with message level security, which requires a username or a certificate.
Now here's the problem.....
-We don't want to have the user log in to the system
-there is no Windows Authentication on the client machines
-and I would use certificates but, we have tons of client machines going out everyday, so installing certificates manually on each machine is not gonna be an option (unless it can all be done through code... and I mean creation and installing)
anybody have any ideas on how to secure this kind of service? Thanks in advance
Peace
In your scenario, it seems like you're looking for the "basicHttpBinding" with no security whatsoever.
<bindings>
<basicHttpBinding>
<binding name="NoSecurity">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
and then configure your endpoints to use that binding configuration:
<system.serviceModel>
<services>
<service name="YourNamespace.YourService"
behaviorConfiguration="MyServiceBehavior">
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="NoSecurity"
contract="YourNamespace.IMyService">
</endpoint>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
There's also a really good blog post series that talk about the basics of WCF security in terms of five different, typical scenarios - excellent read! Your scenario would be the "No security at all" scenario.
Another good introductory article is Michele Leroux Bustamante's Fundamentals of WCF Security.
A more thorough (but also more complex) set of guidance for WCF can be found at the WCF Security Guidance on Codeplex.
Marc
Since you will be using an app wirtten by yourself. You can use wshttpbinding with username and password authentication (Client credential type basic) where your app reads the username and password from a config file.
http://msdn.microsoft.com/en-us/library/ms731299.aspx
EDIT
The windows service could run under an account that the user does not have the password for. The service is in a directory that is protected with ACLs, such that the user of the machine does not have access to the config file.