Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

A simple WCF service with username password authentication: the things you told me

In my last post I described the hassles to get a simple WCF service with username password authentication to work in the real world. Which was not as easy as it looked at first sight. Having weeded out all the unnecessary outgrows of the WCF framework I had it working though the solution was somewhat misty. After that the many useful comments have taught how to nurture my crop and which seeds to plant to turn it into a nice flower bed. Time for a round up.

The problem

The moment you start using authorization, or even authentication, in WCF you have to deal with (X509) certificates. Period. These certificates are used to secure the communication between the WCF service and client consumer. There are many ways to handle this security in WCF. The two most common ways are

  • Transport security. The communication itself is encrypted with the certificate. This boils down to talking over https instead of over http. The problem is that you have to set up some infrastructure here. In the development stage a major issue (for us..) is that https requires a full IIS setup. It does not work with Cassini, the local web server which comes with Visual Studio. Using https in production requires extra maintenance as well, especially when service requests will be coming from a whole different array of sources.
  • Message security. The message is encrypted using the certificate and can now safely travel over any port using plain http. In development this works well with Cassini and in production it makes life easy for system management. The downside of message security is that it requires some special attention setting up. In my last post I described a way to take those hurdles. Right now I will summarize the clearest way to get that done.

Configuring the service

Message security is set up in the configuration of the service. See the previous post for details.

<behaviors>

  <serviceBehaviors>

    <behavior name=“FarmService.CustomerDeskOperationsBehavior>

      <serviceMetadata httpGetEnabled=true/>

      <serviceCredentials>

        <userNameAuthentication userNamePasswordValidationMode=Custom customUserNamePasswordValidatorType=“FarmService.Authentication.DistributorValidator, FarmService />

        <serviceCertificate findValue=Farm storeLocation=LocalMachine storeName=My x509FindType=FindBySubjectName/>

      </serviceCredentials>

    </behavior>

 

Installing the certificate is not that difficult. With the tool Selfcert it’s just a couple of clicks.

The big problem is that when starting up the service host needs access to the private key of the certificate. This private key file is well protected. Misleading is the fact that the service host will have access to this keyfile right after installing the certificate. But it will not always have access the next time the host logs in. Which can lead to a pretty disappointing scenario. You set up the service and all seems to work well. But right after the next reboot (thanks to some kind of update) the service doesn’t work anymore because the keyfile is now inaccessible. In my last post I described a way to fiddle with the access rights. Which was somewhat misty. Thank goodness I’m not the only one who has been struggling with this. There is even a tool in the WIndows Resource Kits which does just the thing we need here.

Windows HTTP Services Certificate Configuration Tool is a command line tool to grant specific users read rights on a certificate’s private key file. It is a quite spartan command line tool. To give myself (and hence Cassini) access rights to the certificate described in above’s config I have to type.

C:\Program Files (x86)\Windows Resource Kits\Tools>winhttpcertcfg -g -c LOCAL_MACHINE\My -s Farm -a gekko-Software\Peter

On IIS the account to grant access to the private key is the default application Pool

C:\Program Files (x86)\Windows Resource Kits\Tools>winhttpcertcfg -g -c LOCAL_MACHINE\My -s Farm -a DefaultAppPool

The tool works well and has (built-in) documentation. Now the service is and stays available.

What UserNamePasswordValidator.Validate doesn’t tell you

Validating the user credentials is done by the Validate method. As long as this method does not throw an exception the credentials will be accepted. In all examples, including mine, this method throws some fancy exceptions like a SecurityTokenException. But the method might also throw some unintended exception. For instance failing to connect to the database when trying to validate the exception there. The problem is that whatever exception is thrown, the client will always receive the same error message:

An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. —> System.ServiceModel.FaultException: An error occurred when verifying security for the message.

This is quite misleading. As you cannot see the difference between unaccepted credentials and a (configuration) bug in your code. What you have to watch out for as well is that this Validate method is invoked before almost anything else, you cannot rely on any configuration being in place yet. As not only myself has found out.

 

Configuring the client

There are two things to a certificate. First of all it has to be in the right format to be read by a client consuming the service. There is no escaping that. The certificates produced by SelfCert are. The next step is that the certificate has to be trusted. Which depends on the location of the certificate in the certificate store. Checking the validity of a certificate is by default a chain which ends in a trusted location. Which can end at Verisign or another root certificate provider. A certificate which will be trusted by everybody on the web has to be bought and is bound to a specific domain. Certificates created with Selfcert are (of course) not trusted.

The nice thing in WCF is that the service consuming client does not have to check the trustworthiness of the certificate. And it only takes just one line of code to do that. Thanks to Yaron, who has a nice blog on WCF and shows up everywhere where there is a WCF issue, for pointing that out.

result.ClientCredentials.UserName.UserName = Farm.FarmUserName;

result.ClientCredentials.UserName.Password = Farm.FarmPassword;

result.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;

The last line does the trick. The client does need a “well formed” certificate but it explicitly states that is does not care about validating it’s origin.

This property can also be set server side. In our case that was not necessary, but experience has learned that it might be needed in your scenario. It can be set server side in the config

<serviceCredentials>

  <userNameAuthentication userNamePasswordValidationMode=Custom customUserNamePasswordValidatorType=FarmService.Authentication.DistributorValidator, FarmService />

  <serviceCertificate findValue=Farm storeLocation=LocalMachine storeName=My x509FindType=FindBySubjectName />

  <clientCertificate>

    <authentication certificateValidationMode=None />

  </clientCertificate>

</serviceCredentials>

And that’s all. In the end it looks so simple.. Just add water.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Wizard!

    Just a small correction. The link to SelfCert has changed due to site redesign, and there is no automatic redirection. This is the correct one: http://blog.pluralsight.com/selfcert-create-a-self-signed-certificate-interactively-gui-or-programmatically-in-net

  • stixoffire

    I had to add a DNSIdentity to my client endpoint in order for the thing to work. Took forever to find that bit of info

  • PeterGekko

    Thanks.

    What you’re describing is the out of the box approach. We store the credentials in the apps (custom) DB to have all info integrated in our app.

  • http://www.facebook.com/people/Joe-Page/506304297 Joe Page

    Great article, but I just have one question. I want my WCf service to have a database to hold user information and check against it. With a way to manage the users. I was thinking using the customer username password validator and basically use the default database within the web application that is going to host the service. Is that the proper approach? Or is their an out of the box way to do this.

  • PeterGekko

    It’s an enum in .net. http://msdn.microsoft.com/en-us/library/system.servicemodel.security.x509certificatevalidationmode.aspx

    Every value has a corresponding int value. Provided the enum follows default emu values, none would be 2.

  • jbk

    result.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
    IN JAVA ?????

  • Ganesh

     PeterGekko,

    Thanks for your reply. Yes you are right, after upgrading the JDK, there are no errors.

    Regards,
    Ganesh

  • Anonymous

    I don’t know much about Java but nevertheless I think the problem lies in Java itself. You need some Java stuff which understands the policy coming in.

  • Ganesh

    Nice article. But if the service is consumed by a java client, it says could not serialize the policy.. Any thoughts on this please?

  • http://www.facebook.com/shawson Shaw Clint Young

    Nice- your selfcert link needs updating though.. http://blog.pluralsight.com/2012/02/13/selfcert-create-a-self-signed-certificate-interactively-gui-or-programmatically-in-net/

  • Anon

    Thanks Man, that really helped me a lot.

  • Julius

    Yes, but there is no security enabled by default for this binding. You’ll have to configure all security settings yourself if you use this binding. (WSHttpBinding features security enabled by default.)

  • Dinesh

    Hello Peter, 

    This has been an year old post but is still of great help :)

    Thank you for putting it all together. 

    I just wanted to add here that the Certificate ( Create/Permissions ) requirement are very well documented on MSDN Site.
    http://msdn.microsoft.com/en-us/library/cc949011.aspx#Step6

    Thus, we need not give permissions from windows properties and there is a 6 Step process that will make it work. 

  • http://pulse.yahoo.com/_RVGRT33GCBAJW2GUSEER7ORER4 Russell

    Great article, thanks for the information!  I’m going to try this out with IIS Express.  It’s supposed to support SSL and get around the Cassini limitations you mention.

  • Mumtaz

    indeed a nice one !! :)  is it necessary to use wshttpBinding ??? will the same work with basichttpBinding?? I think NO … am i right ?? if yes please could you tell me why basichttp can’t be used ? and if no ,could you explain me that case as well !! iam srry but wcf is actually complicated :)

    Mumtaz

  • Anonymous

    Thanks for sharing.
    When it comes to settings like MAxReceivedMessageSize, there are more settings like that which affect the same (mis-)behavior. That’s a band of sport on itself.

  • Mikkel

    Damn… It eats my XML – here is an image of it in stead :)

  • Mikkel

    The XML in my comment was messed up, here is the right XML:

  • Mikkel

    Also, if you encounter the error “The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.” in the client, simply add this line to the setup of the binding object:

    binding.MaxReceivedMessageSize = 5000000;

    You can also add the maxReceivedMessageSize property to the vbinding element in the app.config of the client like so:

  • Mikkel

    Please note that in your first post, you are using the certificate storage “TrustedPeople” – in this example you are using “My”.

    New readers of this blog, be aware that you will need to generate a new certificate in the “LocalMachineMy” storage in order for the example above to work.

    Also be aware that after messing with certificates Visual Studio 2010 might need a restart, to refresh the certificates in cache (?).

    To delete a certificate again use this command in VS Command Prompt:
    certmgr.exe -del -c -n CertificateName -s -r localMachine TrustedPeople
    – or –
    certmgr.exe -del -c -n CertificateName -s -r localMachine My

    Here is also an MSDN article covering this (not too much about certificates though):
    http://msdn.microsoft.com/en-us/library/aa702565.aspx

  • Anonymous

    A WCF service is per default stateless. Every call to one of its methods is independent of the others. Every call could come from the same or from another client. The service does not know, the service does not care. That’s why you have to provide user credentials with every call.
    There is a possibility in WCF to give sessions a state. Which makes things more complicated, for instance you have to take care of things like session expiration.
    Far simpler is to store the user credentials on the client. FI : with the first call the user enters the credentials in a couple of textboxes. Copy these values to a private variable and reuse these on ever call of a service. The user is only confronted with the credentials once, the programming effort is very small. You can also store the credentials anywhere you like. To the service call they are just two strings :)
    I cannot make things simpler than this

  • Nupur

    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

  • cav

    Other issues that kept me going for a week:

    -Apparently, WCF doesn’t work with IIS 7 OOTB. So you need to run command prompt as administrator, and run the following command –

    “%windir%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\ServiceModelReg.exe” -r -y

    -After I got through the certificate stuff I was still getting HTTP error 503, “Service unavailable”. That ended up being leftovers from my previous attempts, still listening to ports:

    http://blogs.msdn.com/webtopics/archive/2010/02/17/a-not-so-common-root-cause-for-503-service-unavailable.aspx

    So much for WCF making everything so easy. I found many pilgrims on my travels who, like me, took a week to get hello world working with SSL because of undocumented garbage.