Wednesday, March 09, 2011

WCF Client Server Application with Custom Authentication, Authorization, Encryption and Compression

Introduction

With the appearance of Windows Communication Foundation building Service Oriented Applications became easier than ever. And lots of articles poured in with extensions for special cases. Even so, there are still situations left untreated. Like the following I had to resolve:
- Client-server application – http protocol – NO IIS
- Authentication – user/password from a database – NO SSL/X509 certificate
- Authorization – roles from a database
- Encryption for the credentials (with option for the entire request/response)
- Compression for both the request and response.

Logical solution

For each of the above requirements/restrictions we have:
- No IIS – we need our own http server – can be easily done by WCF in a few lines of code - no need to insist on this.
- User/Password authentication – this easily done by default, but an X509 certificate is required.
Therefore we need our own mechanism: we’ll add the credentials to the header of the message and encrypt them (or the entire message).

- Encryption – we’ll use an asymmetric algorithm[1] (RSA) with public/private keys. Usually it is needed that the server and the client have their own set of public/private keys to encrypt both the request and the response but we’ll use an artifice to avoid the client set of keys.
With RSA only a small amount of data can be encrypted (for 2048 bit encryption only 128 bytes of data). Therefore it is used to encrypt a random generated password that will be used by a symmetric algorithm[2] (AES) to encrypt the message. The server decrypts with its private key, the password of the client, with that password decrypts the message. And using the same password encrypts the response message.

Additional security for the credentials – even if the credentials are encrypted, a listener can get those encrypted credentials and launch a new request, therefore we’ll add an expiration date.

- Compression – we’ll use gzip compression/decompression just before sending/receiving the message and the response.

Now, let’s see how the above generic considerations look in the client-server message flow:

1. Server starts; a new RSA key is generated (or loaded from the disk).
2. Client starts; it asks the server for the public key and time; based on server time and client time it will calculate the client-server-timespan.
3. Client prepares the request message
a. The credentials are added to the message header; the Credentials token will have User/Password/Expires properties; the Expires will be calculated as client time + client-server-timespan + a few seconds;
b. The message (or only the credentials part) is encrypted; more exactly:
- a random key is generated and saved along with the message id;
- the message (or the credentials part) is encrypted with the AES algorithm using the previous generated key;
- the AES’ key is encrypted using the server’s RSA public key and added to the encrypted message
c. The message is compressed
4. Server receives the request message
a. The message is decompressed
b. The message is decrypted; more exactly:
- the client AES’ key is retrieved by decrypting using the server’s private key;
- the message is decrypted using the client key;
- the client’s key will be saved along with the message id – it will be needed to encrypt the response with the same id;
c. The credentials are extracted from the message; the Expires is compared with server’s current time and if it is bigger an AuthenticationException is thrown; 
d. Authentication – the credentials are verified against a database;
e. Authorization – the roles of the authenticated user are retrieved from a database/cache.
5. Server prepares the response message
a. The message is encrypted; more exactly the message will be encrypted (AES) using the client’s key saved during decryption of the request; 
b. The message is compressed
6. Client receives the response message
a. The message is decompressed.
b. The message is decrypted using the key saved during encryption (3.b.)
WCF considerations
How to extend WCF to implement the above flow:
On the client, for adding the credentials we’ll use a BehaviorExtensionElement that implements IClientMessageInspector which has a BeforeSendRequest method (see implementation here).

Read more: Codeproject