Using Certificate-based Authentication and Protection with Windows Communication Foundation (WCF)

This week I have been working on a project where two systems that exist in separate security domains need to be able to communicate sensitive information securely. The communications platform that I was using was Windows Communication Foundation (WCF).

I first started by implementing my web-service in WCF using the standard “basicHttpBinding” which doesn’t provide (by default) any authentication or message protection support. I did this because I wanted to focus on the implementation of my business logic first and then see whether the promise of WCF holds true about just being able to turn on security.

As far as security options go I could use either message or transport level security. I’m more of a message orientated guy so I opted to go down that route which meant that I could choose between the following approaches for authentication and protection:

  • Windows Authentication (SSPI context token)
  • Username and Password (only for authentication)
  • Certificates.

Out of that list the first one is definitely not viable because there is no way to establish a trusted connection and the second option whilst providing authentication couldn’t provide message protection and would need to rely on certificates.

Since I would need to be working with certificates I decided to just use them for everything (including authentication), but the next challenge was – how do I go about it?

A Simple Starting Point

I am a big fan of starting with something simple that works and then introducing only necessary complexity from there, so for the purposes of this little blog post I am going to expose a “hello world” service and expose it first using the “basicHttpBinding”. The service contract looks like this:

image

Given such a simple contract, the implementation should be fairly obvious:

image

I like to host my services inside console applications initially because I find it makes them easier to debug, then as I get ready for production I like to convert that console application into a Windows Service so that I can easily stop and start the web-service and know that it is definitely not available. The hosting code inside a console application for this is fairly basic:

image

All of these classes exist within the “ServerSide” namespace, although if you are building this service for real the host program, the service contract, and its implementation might be in different assemblies, and possibly different namespaces.

If you tried to execute the program now it would simply fall over because there is no matching configuration entries in the application configuration file – here is what it should look like when it crashes as a point of reference:

image

The minimal configuration file to get this working is presented below. The easy way to remember what you need to do is repeat “ABC” to yourself – address, binding, and contract.

image

In this case the address is derived from the base address “http://localhost:8999/HelloWorld/“, the binding is “basicHttpBinding” and the contract is the interface that the HelloWorld class implements, “IHelloWorld”. When we instantiate the service host, it uses the type name of the implementation class to find the <service /> node and then determine how to expose each service contract.

If you happen to be running Windows Vista you will need to run Visual Studio with administrative permissions to do this work. The reason is that self hosted services use HTTP.SYS (background) which allows multiple processes to listen on the same address and port, but map different URL spaces – but in order to do the mapping your process needs to be granted sufficient rights or it needs to be set up manually before hand. It is far easier to run with administrative rights during development in this case.

Whilst this service will start up and run we need some way of providing client applications a way of generating their web-service proxy classes and to do that we need to expose a WSDL contract and some service metadata. This means more configuration changes.

To do this we simply add a custom behavior to the service which tells the WCF plumbing to expose the WSDL contract:

image

Once this is done, all we need to do is hit the service with a browser at “http://localhost:8999/HelloWorld/” and we get the following page:

image

The link on that page will allow you to pull down the WSDL contact and we can now use this build up our client code. In this case my client program is just another console application to which I add a service reference (right mouse click on the console project and select “Add Service Reference…”).

image

I’m actually using Visual Studio 2008 Beta 1 on this machine so if you are using Beta 2 the dialog is a little bit different, but basically works the same way. You input the address of the web-service (no need to append the “?wsdl”, as it will auto-discover) and then give the reference a name. I typically give it the name of the service and append the word “Proxy” to avoid any ambiguity.

Once that is done implementing a test client is pretty simple:

image

One of the things that I don’t like about the way that the WCF proxy generator works is how it tends to over complicate the client-side configuration file. I’m not going to post what it generates here for the sake of brevity (going well so far eh?), but I will post a sample of what it can be reduced to:

image

All the settings that get generated have a place, but unless you need to tweak them you can probably live with the defaults. The code above basically replicates the client version of what the server has, except the client doesn’t expose any metadata and the name is what is used by the WCF proxy to find its configuration (in a world where you might have WCF calls going to different machines with different configurations).

If we run both the client and server now we can see that we have a simple working solution where the client is contacting the server and displaying its output.

image

Now that we have a simple working solution its time to look at getting a solution that uses certificates to authenticate the client and server with each other might look like.

Creating the Certificates

Before we can start using certificates to authenticate the client and server we actually need two certificates that we can use, one for the client and one for the server. To do this we use the “MakeCert.exe” utility with the following arguments (note, it is executed twice):

image

The first certificate that was created will be used by the server to authenticate itself to the client, and the second certificate will be used by the client to authenticate itself to the server.

Here is a brief explanation of what the various switches mean:

  • -pe tells us that the certificates will be exportable which will be useful because we will need to move them around.
  • -ss tells us which store to put them in, in this case My = Personal.
  • -sr tells us which part of the registry to put them into, in this case we are going for local machine because ultimately we would want to run this stuff as a Windows Service and having to edit the store for a specific user account is too much of a hassle (at least for a demo).
  • -a tells us what kind of algorithm we are going to use for hashing.
  • -sky tells us what the purpose of the keys are, in this case we are going to use them for encryption (exchange) rather than just signing.
  • -n tells us who the key is issued to. In the enterprise this is often the server hostname, but this is all on one box so we will issue it to ServerSide and ClientSide and then override the DNS identity mapping.

Now that the certificates are created we need to export them and re-import duplicates into the location that WCF expects to find them. This is actually where things get a little bit confusing and its certainly where I often find myself scratching my head.

We first need to bring up the Microsoft Management Console which allows us to browse the local machine’s cache of certificates. To do this launch “mmc.exe”, and you should be presented (after the UAC goodness in Vista) a window titled “Console1”.

image

We can’t use the “certmgr.msc” file because it is limited in that it only opens the current user certificate store. From “Console1” we need to add the Certificates MMC Snap-in, and when you do that you need to make sure that you are adding it for the computer, not the current user.

image

Once that is done you can complete the wizard and OK your way out to a functional MMC window, at which point you need to navigate to the “Personal” node to see our client side and server side certificates.

image 

There are actually four different occasions where these certificates will be used.

  1. The client will use ClientSide to represent its identity to the server.
  2. The server will use ClientSide to validate the clients identity.
  3. The server will use ServerSide to represent its identity to the client.
  4. The client will use ServerSide to validate the servers identity.

Truth be told, this isn’t exactly how it would work in a fully fledged production scenario.

What would actually happen is that the server and client would validate each others identity by following a chain of trust associated with each certificate to a root certification authority which they both trusted (although it might be a different CA for each certificate as Windows can trust multiple). Over the Internet this might be a public one like VeriSign, or it could be a root CA inside the enterprise – in any case it is actually unlikely that the server would have access to the client certificate and vice versa.

What we will actually do to avoid all this infrastructure burden is use a “peer trust” model where each side does indeed have access to the certificates and can validate the messages being sent.

In order to support this we need to export the certificates from the certificate store and import copies into the TrustedPeople store so that WCF can find them for validation purposes.

When you export the certificates there is no need to export the private key as this is not required for validation purposes. Once you have finished this process, the TrustedPeople node inside the certificate store should look as follows (although obviously you won’t have a certificate issued to “mitch.denny” – or at least I hope not!):

image

Once this is done we can start the process of cutting our simple working demonstration over to using certificates. Sometimes I reflect on what certificates haven’t become more popular for signing things like e-mails, but when you have to go through the above it becomes a little bit clearer.

Configuring the Server

The first step in configuring the server is telling it that we want to protect the message using certificates. The easiest way to do this is switch over to the “wsHttpBinding” and customize the binding. The picture below shows the added <bindings /> element and the modified <endpoint /> element:

image

Next we need to tell the server which certificate it will use to identify itself, which certificates it can accept from the client and the fact that we are going to be using a peer-trust certificate validation approach. To do this we extend the existing “customBehavior” that we used to get metadata exchange working (WSDL contracts):

image

Notice that we pull the server certificate from the “My” (or Personal) store. Also notice that I haven’t specified anything about the client certificate. This is because of the <authentication /> element telling WCF to do peer-trust which means WCF will look for the certificate (containing only a public key) that the client provides in the “TrustedPeople” store.

Each store has a different configuration of certificate based on what we did setting them up. The “My” store contains a copies of the certificates with the private key, where as the “TrustedPeople” store contains copies of the certificates with only the public keys. You can see the Public Key Infrastructure underpinning all of this.

Now that we have the server configured, we need to look at the client because it needs to know to send the messages in the right format and also which certificates to use.

Configuring the Client

The client configuration is essentially a mirror image of the server configuration file, except that it will only issue one client certificate and it only needs to validate one server.

Once again we have to update the binding configuration to use a custom version of “wsHttpBinding” that uses certificates to support message level security.

image

Because the client doesn’t host the service we then need to configure a custom behavior on the endpoint. This custom behavior definition didn’t exist before like it did on the server so it needs to be added from scratch.

image

We are now almost there. If we run the code it will still crash, but for a relatively minor reason. That reason is that we are attempting to communicate to a host called “localhost”, yet the server is identifying itself as “ServerSide” as specified in the certificate. The WCF infrastructure considers this to be an error (correctly). However it is fairly common that this happens, especially with temporary certificates or within the enterprise. To work around this we explicitly tell the client endpoint definition what identity assertion to expect from the server, regardless of what hostname we are contacting it on – this is yet another configuration entry:

image

Finally, with all of the configuration done we can now run the program and see that the client is now securely communicating to the server. The most obvious sign that something is different is the extra time it takes to execute the code. This is because the handshake between the client and server takes more processing power and bandwidth.

image

Summary

Setting up secure, certificate based communications between a client and server using WCF can take a little bit of effort. Most of the time it is because we only do this configuration every now and then and when subsequently forget where the certificates need to go, and how to configure WCF to do precisely what we want. One of the reasons I wrote this post was because I had trouble finding a simple example of how to get this working on the web to refresh my memory, so hopefully you will find this useful as you are trying to remember how to do this yourself.

P.S. If you like a copy of the code I used for this post you can download it from here.

61 thoughts on “Using Certificate-based Authentication and Protection with Windows Communication Foundation (WCF)

  1. Jeff Wharton

    Mitch,

    Forgot to point out that we had lots of problems using x509FindType=’FindBySubjectName’ as it uses a case-insensitive string compare and finds all certificates with the subject name containing that string, regardless of other subject values. It is recommended to use ‘FindBySubjectDistinguishedName’ or ‘FindByThumbprint’ to guarantee uniqueness.

    Also, ‘FindByThumbprint’ is one of the options supported by SAML as a method of guaranteeing uniqueness.

    Cheers
    Jeff

  2. Pingback: Configuring Visual Studio 2008 for ASP.NET AJAX 1.0 Development « notgartner

  3. Mitch Denny Post author

    Hi Jeff,

    Thanks for the pointers, and in fact I did remember that you guys were doing this at DITR. It was funny because that is why I know I had gotten this working before because I demonstrated it one day to the team (before you arrived actually).

    Oh well – at least I have a working demonstration – thanks for the pointers about FindBySubjectName, I hadn’t come across those issues but you guys are doing a lot more certificate work than I normally do.

  4. chikky

    well very nice tutorial … i tried it too but i was not able to get through and i dont know why even after adding the identity tag it was reporting the same error.

  5. Peter

    Nice tutorial.
    Today I’ve found myself fighting with those stupid certificates.
    I’m using message security layer with UserName credentials.
    The certificate I’ve created for server stops working – it works one day and another I get an exception (something with chain of trust).
    I don’t know what to do…

  6. Pingback: Securing a WCF service with Certificates « The Wandering Glitch 2

  7. Ron

    Mitch,

    I’m struggling with trying to use the SQLMembershipProvider for authorization/authentication in WCF over TCP (not HTTP). Documentation is sparse on this and what little there is does not work. Do you have any (complete) samples on this?

    Thanks,
    Ron

  8. Mitch Denny Post author

    Hi Ron,

    Unfortunately I don’t have any samples in my back pocket for this. You’ll probably need to implement your own UserNameTokenManager though.

  9. Harendra

    This is an excellent article on the use of certificate. I have been through few good books (WCF Programming, Learning WCF, PRO WCF, WCF Unleashed) and few other sites but your article is simplest and most effective one to get basics right. Well done might.

    I may produce few on future as I move forward, in particular on using ‘SQLMmevership’ provider for authentication/authorization.

  10. Brett

    Good article. I notice there is no mention of adding windows security access to the created certificate’s private key for the process running this code. Is this necessary? I cannot get this to work on my machine without this step.

  11. Wolf

    I have used this code for a sample web application, without writing web.config… and it work fine.
    In this manner you can write only server side configuration part.


    localhost.Service1Client Sample = new localhost.Service1Client();
    Sample.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine,
    StoreName.My, X509FindType.FindBySubjectName, "ClientSide");
    Sample.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerTrust;

    Response.Write(Sample.MyOperation1("sample value"));

  12. Anderson

    How can you check on the server side for a particular client certificate, say we need to check not only that a certificate is authentic, but that that specific certificate has been approved for use (ie in a DB “user” table)?

  13. Niti

    How do we know all of this actually doing anything? Did you actually inspect the SOAP message. Also, what exactly is this achieving. Are you encrypting, signing or authenticating?

  14. Kelsey

    Nice example, much appreciated!

    I had my service hosted under IIS, and I found I had to do the following, in order for the ASPNET user to see my Serverside certificate:

    winhttpcertcfg -g -c LOCAL_MACHINE\My -s ServerSide -a ASPNET

    You can download the winhttpcertcfg from Microsoft.

  15. Sahaj

    Hi,

    I am facing problem with Certificates. I am trying to communicate IIS With Winform based application using certificate.

    —-Objective—-

    Our Windows Form Client Application will act as WCF Service with Certificate as ClientcredentialType which will interact with WCF Service running on IIS Server.

    While running the above attached code in our application I am facing error “Caller is not Authorized”(Screen shot attached:Error.jpg)

    On Client Side is this correct:
    EndpointAddress address = new EndpointAddress(new Uri(“http://” + userSessionListItem.ComputerName + “:7000/Service/SubscribeService”),EndpointIdentity.CreateDnsIdentity(“WinClient.Shell”)); // WinClient.Shell is name of Windows Form Client Application Certificate with which we will interacting.

    The method implemented by this WCF Service is called by a WCF Service running on IIS Server. We have to run our application in both intranet and internet scenario.

  16. Mitch Denny Post author

    Hi Sahaj,

    I’m not really sure from what you have provided. You will need to create a DNS identity for the “server” which in this case is the client software. But it depends on whether you’ve got the certificates created correctly.

  17. Andy

    Hey
    Thanks a Zillion . I was struck with this for more than a week and your tutorial made it very easy.
    Just a question , IF i were to set up a TLS or SSL connection with WCF , how should i proceed any pointers

  18. Jason

    This is a great example…but i have one question. If i wanted to test this example across 2 machines where would i run the makecert (i assume on the server that is hosting my WCF Service). If that is the case, do i run both makecert commands and export the client cert to the client machine running my windows application? Thanks.

  19. Ramesh

    very nice article, really helpful to my work. could you please send if you any other articles also on WCF and others.

  20. Igor

    Hello,

    I am trying to use cert based message level security with basicHttpBinding. I used your example. It works fine with wsHttpBinding. But if I switch to basicHttpBinding I get the error:

    The service certificate is not provided for target ‘http://localhost:8999/WcfCertificatesTest’. Specify a service certificate in ClientCredentials.

    What am I missing? So far I know basicHttp should support cert based message security.

    Thanks,

    Igor

  21. Igor

    Hello I found a solution (I guess). On Client side I added in the clientCredentials element in the ServiceCertificate the node defaultCertificate.

    Now it is working.
    Now the question is if it is correct configuration???

    Thanks.

  22. Neil

    Hi,

    Thanks for the excellent post, far better explained than anywhere else I have seen. I have a little bit of a query hopefully you can shed some light for me.

    I need to use IIS and SSL. But only to validate that a client has the appropriate certificate to connect to the server. The client does not need to validate the servers authenticity. Do you know how I can achieve this?

  23. achu

    Hi Denny,

    Brillient!

    I wish microsoft hire you to write some article about how security in wcf really works.

    thanks,
    achu.

  24. Brent Craighead

    Hi,
    I tried to implement in my solution, but I get the following error message:

    Error in deserializing body of request message for operation ‘GetUpcomingRaces’. OperationFormatter encountered an invalid Message body. Expected to find node type ‘Element’ with name ‘GetUpcomingRaces’ and namespace ‘http://tempuri.org/’. Found node type ‘Element’ with name ‘e:EncryptedData’ and namespace ‘http://www.w3.org/2001/04/xmlenc#’.

    I am using basicHttpBinding, message security mode and PeerTrust as certificateValidationMode.

    Here are my system.serviceModel sections for my config files.

    Client:

    <!—->

    Service:

    Thanks,
    Brent

  25. Praveen

    Thanx a lot!
    I couldn’t find better article on the web to implement Certificates based Meessage security but this….
    cheers!!!

  26. Nupur Bakshi

    Hello,
    I have created a Service based application using Message Based Security Over UserName Client Credential Type in WCF.I have also created certificates and accesses claims to the clients using IAuthorization Policy of Claim Based Transformation.My problem is to authenticate client only once for the initial service call and then using he same credentials of Client for other service call.Please provide me the solution asap.

  27. Nupur Bakshi

    Hello Sir,
    I am developing a project based on WCF in which I am implementing WCF Message Based Security with UserNamePassword as Client Credentials.I have also created X509 certificates and Claim Based Authorization to provide access to Claims.Everything is working fine as when I initiate the call to a method of the service it asks for the authentication of the client and after authenticating it provides access to the method of the service.But when I call another method of the same service it again asks for re-authentication of the client as I don’t want to authenticate the client for every service call.Please help me in this by providing step by step solution on how to do it.

  28. Nupur Bakshi

    Hello Sir,
    I am developing a project based on WCF Message Based security with UserNamePassword Client Credentials and Binding used is wsHttpBinding.I have also created X.509 certificates and providing access to claims using Claim based Authorization.Everything is working fine but my problem stands on re-authentication of client for every method call and providing client credentials for every method call.I want to tell you that I have taken 2 buttons on the interface.Both buttons are calling different methods of same service.In one button I have passed Client Credentials and in another one I simply created an instance to call the method.When I click on the button in which Client credentials are passed it calls the method after authenticating the client but when I click on the button in which only instance is created it throws an error “UserName is not provided.Specify UserName in Client Credentials”.I don’t want to re-authenticate the client on every call of the method.If I am doing wrong provide me the full guidance on how to implement it and do take into consideration that I am new in WCF so provide me the step by step and complete help.I am not getting the way on how to provide the credentials of the client just once so that every method can be accessed after that.Please help me in this ASAP.Thanks

  29. Arif

    Hello,
    It was indeed a very good article, the only thing is I could not find here is which certificate is used for authentication and which certificate is used for encryption

    1. Client certificates are not necessary for encrypting messages to server
    2. In Service Config, is not meant to indicate which certificates it can accept from the client,
    It is used in duplex communication scenario where Service needs to communicate with client, without client initiating the communication to service
    Refer below link, Service Credentials section
    http://msdn.microsoft.com/en-us/library/ms731199.aspx

    Please let me know your views on the same
    Thanks

  30. Alx

    Hello,

    I’ve made all this tutorial but i still have a problem.

    I’m on a network, the server runs on my PC like a console APP, clients are in different PC (same network).
    When I launch server & client both ON MY PC, IT’S WORK with certificate…

    But When I launch Server on my PC and Client on an other PC in the same network, FAIL !!!

    “SOAP SECURITY NEGOCIATION FAIL.
    Certificat CN=ClientSide need to have a private key. The process need access on the private key.” (Translate from the french exception sry)

    I have create this certificate on server :

    makecert -pe -ss My -sr LocalMachine -a sha1 -sky exchange -n “CN=ServerSide” “D:\dadCommercialProject\appServer\cert\ServerSide.cer”
    makecert -pe -ss My -sr LocalMachine -a sha1 -sky exchange -n “CN=ClientSide” “D:\dadCommercialProject\appServer\cert\ClientSide.cer”

    after I’ve export it and import it on “Allow Person” on Server.

    On client I have import it in “Personnal” but i cant import it in “Allow Person” …

    ANY SOLUTION ?

  31. Max Pavlov

    What if you had a few different clients? How would you go about separating them logically for, let’s say, authorization purposes, on the server?

    Can you get a unique client certificate ID so it can be recored to a data store on the server and used for separation if future?

    Thanks.

  32. Andreas Sommer

    Did you try this with a chain trust environment? With the client certificate in “Trusted People” and “PeerTrust” like you described, everything works as expected. But if I put the server’s certificate in “Trusted Root Certification Authorities” and use “ChainTrust”, I get the error “The caller was not authenticated by the service.”. Any ideas?

  33. jyoti

    Hi,
    please help me that how to attach client provided certificates with outgoing request in .net code.
    I am getting error message that “com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5720E: A required message part [body] is not signed.”
    please help.
    thanks

  34. Sarabjeet Singh

    Hi Mitch, I tried to follow your article….but i a come up with this error ..

    ERROR:
    “The request for security token could not be satisfied because authentication failed.”.

    Please help me out..because I have wasted almost 6-7 hours in this..

  35. sarabjeetsingh2012

    Hi Mitch..I tried to followw the steps….but I get this error..

    ERROR:
    “The request for security token could not be satisfied because authentication failed.”

    Please help me out…I have wasted almost 6-7 hours after this error…

  36. Deepak Agarwal

    Hi Sarabjeet,

    Use on both client and server’s config. However, this is only for testing purpose and should not be used in production-like scenarios.

    Thanks!
    Deepak Agarwal.

  37. Deepak Agarwal

    Hi Sarabjeet,
    Use certificateValidationMode as None on both client and server’s config. However, this is only for testing purpose and should not be used in production-like scenarios.

  38. Pingback: What's wrong in supplement slight to countenance date format in yyyy-dd-mm? - Abram

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s