Click here to Skip to main content
15,885,985 members
Articles / Programming Languages / C#

Custom Authentication and Security for Routing Service of WCF 4.0

Rate me:
Please Sign up or sign in to vote.
4.50/5 (4 votes)
13 Oct 2012CPOL8 min read 44.1K   1.3K   30  
Defining security contexts in Routing service environment
Though WCF routing features for content-based routing, protocol bridging, and error handling seem to be very exciting, I have faced some issues while defining their security context in Routing service environment. In this article, we will discuss custom authentication and security for routing services.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.ServiceModel.Channels;
using System.Text;
using System.Xml;

namespace GlobalCommonLib
{
    public class CryptographerBase
    {
        public const string KeyElementName = "EncryptionKey";
        public const string EncryptedElementName = "Encrypted";

        //The elements that will be encrypted when the contentEncryption is set to "Credentials" or "All".
        public const string CredentialsElementName = "Credentials";
        public const string AllElementName = "s:Envelope";

        public const int AesKeySize = 256; //The minimum size of the key is 128 bits, and the maximum size is 256 bits. [2]
        public const int RsaKeySize = 1024; //The RSACryptoServiceProvider supports key lengths from 384 bits to 16384 bits in increments of 8 bits if you have the Microsoft Enhanced Cryptographic Provider installed. It supports key lengths from 384 bits to 512 bits in increments of 8 bits if you have the Microsoft Base Cryptographic Provider installed.[1]

        protected const bool Content = false;//Encrypt only the content (true) or the node also (false); it seems not to function on true.?!?

        //on server: generates a new public/private key at its instantiation
        //on client: must be initiated with the public key of the server 
        public static RSACryptoServiceProvider RsaServiceProvider { get; private set; }

        //this is necessary on a multithreading environment to pair the request and reply (both on server and client)
        //on client: contains the message id and the key used to encrypt the request message; it will be used to decrypt the reply message.
        //on server: contains the message is and the key extracted from the encrypted message; it will be used to encrypt the reply message.
        //protected static ConcurrentDictionary<string, byte[]> AesKeys { get; private set; }

        static CryptographerBase()
        {
            RsaServiceProvider = new RSACryptoServiceProvider(RsaKeySize);
            //AesKeys = new ConcurrentDictionary<string, byte[]>();
        }

    }

    public class Cryptographer : CryptographerBase
    {
        public static void Encrypt(XmlDocument xmlDoc, string elementToEncrypt)
        {
            XmlNodeList elementsToEncrypt = xmlDoc.GetElementsByTagName(elementToEncrypt);
            if (elementsToEncrypt.Count == 0)
                return;

            var aesServiceProvider = new AesCryptoServiceProvider {KeySize = 256};
            aesServiceProvider.GenerateKey();

            XmlNode idNode = xmlDoc.GetElementsByTagName("a:MessageID")[0];
            
            string id = string.Empty;
            if (null != idNode)
                id =  BitConverter.ToString( Encoding.UTF8.GetBytes(idNode.InnerText));
             id += "||" + BitConverter.ToString(aesServiceProvider.Key);
            
            //AesKeys.TryAdd(id, );

            var xmlElementToEncrypt = (XmlElement)elementsToEncrypt[0];
            var encryptedXml = new EncryptedXml();

            byte[] encryptedElement = encryptedXml.EncryptData(xmlElementToEncrypt, aesServiceProvider, Content);

            var encryptedData = new EncryptedData
                                    {
                                        Type = EncryptedXml.XmlEncElementUrl,
                                        EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url)
                                    };
            var encryptedKey = new EncryptedKey
                                   {
                                       CipherData =
                                           new CipherData(EncryptedXml.EncryptKey(aesServiceProvider.Key,
                                                                                  RsaServiceProvider, Content)),
                                       EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url)
                                   };
            encryptedData.KeyInfo = new KeyInfo();
            encryptedKey.KeyInfo.AddClause(new KeyInfoName(KeyElementName));
            encryptedData.KeyInfo.AddClause(new KeyInfoEncryptedKey(encryptedKey));
            encryptedData.CipherData.CipherValue = encryptedElement;
            encryptedData.Id = id;
            EncryptedXml.ReplaceElement(xmlElementToEncrypt, encryptedData, Content);
        }

        public static void Decrypt(XmlDocument xmlDoc)
        {

            XmlNodeList encryptedElements = xmlDoc.GetElementsByTagName("EncryptedData");
            if (encryptedElements.Count == 0)
                return;

            var aesServiceProvider = new AesCryptoServiceProvider();
            var encryptedElement = (XmlElement)encryptedElements[0];

            var encryptedData = new EncryptedData();
            encryptedData.LoadXml(encryptedElement);

            var keyinString = encryptedData.Id.Split(new string[] {"||"}, StringSplitOptions.None).ElementAt(1);
            byte[] key = keyinString.Split('-').Select(b => Convert.ToByte(b, 16)).ToArray();
            //AesKeys.TryRemove(encryptedData.Id, out key);
            aesServiceProvider.Key = key;


            var encryptedXml = new EncryptedXml();
            encryptedXml.ReplaceData(encryptedElement, encryptedXml.DecryptData(encryptedData, aesServiceProvider));
        }

        public static ArraySegment<byte> EncryptBuffer(ArraySegment<byte> buffer, BufferManager bufferManager, int messageOffset, string elementToEncrypt = "s:Envelope")
        {
            var xmlDoc = new XmlDocument();

            using (var memoryStream = new MemoryStream(buffer.Array, buffer.Offset, buffer.Count))
            {
                xmlDoc.Load(memoryStream);                
            }

            Cryptographer.Encrypt(xmlDoc, elementToEncrypt);
            byte[] encryptedBytes = Encoding.UTF8.GetBytes(xmlDoc.OuterXml);
            byte[] bufferedBytes = bufferManager.TakeBuffer(encryptedBytes.Length);
            Array.Copy(encryptedBytes, 0, bufferedBytes, 0, encryptedBytes.Length);
            bufferManager.ReturnBuffer(buffer.Array);

            var byteArray = new ArraySegment<byte>(bufferedBytes, messageOffset, encryptedBytes.Length);

            return byteArray;
        }

        public static ArraySegment<byte> DecryptBuffer(ArraySegment<byte> buffer, BufferManager bufferManager)
        {
            var xmlDoc = new XmlDocument();
            using (var memoryStream = new MemoryStream(buffer.Array, buffer.Offset, buffer.Count))
            {
                xmlDoc.Load(memoryStream);
            }

            Cryptographer.Decrypt(xmlDoc);
            byte[] decryptedBytes = Encoding.UTF8.GetBytes(xmlDoc.OuterXml);

            byte[] bufferedBytes = bufferManager.TakeBuffer(decryptedBytes.Length);
            Array.Copy(decryptedBytes, 0, bufferedBytes, 0, decryptedBytes.Length);
            bufferManager.ReturnBuffer(buffer.Array);
            var byteArray = new ArraySegment<byte>(bufferedBytes, 0, decryptedBytes.Length);

            string s = Encoding.UTF8.GetString(byteArray.Array, byteArray.Offset, byteArray.Count);
            return byteArray;

        }

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader PracticePRO Software Systems Inc
United States United States
In my childhood, my uncle has shown me how to see the cloud in a close look and I understand that one can draw some elements of the Earth in the sky-canvas if he/she wants to. After that the cloud becomes closer to me and It teaches me one thing that, a deeper-look to something will give you some clues to draw your imagination. You can able to see that one which you have build-up in your mind.

Years past, I have started my career as a software engineer and has been looking for passion in my coding and development which I should be to enjoy my profession and has started asking myself- 'am I doing any engineering here?!' Is my code becoming that thing which I have designed in my mind? So to find that answer I have tried that old solution here... I have decided to come closer to my code and start analyzing them. And it is really working for me and at least it gives me the confidence that I can build something that I really want to. I can draw my thinking there through my code and can build-up my vision that I have designed in my mind. It also helps me to think out of the box, solve each problems by making blocks and make me careful on each steps.

• Morshed's Technical Blog site: http://morshedanwar.wordpress.com/

• Morshed's Technical articles those are published in Codeproject site: http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=2992452

• Morshed's Linkedin profile: http://www.linkedin.com/in/morshedanwar

• Morshed's Facebook Profile : http://www.facebook.com/morshed.pulok

Beside all these I like to do - photography and music. Here is my Flickr photos : http://www.flickr.com/photos/morshed_anwar/

Comments and Discussions