Monday, July 28, 2008

Secure/Encrypted cookies in ASP.NET with System.Cryptography

In today's world one cant transmit a digital syllable without some kind of protective wrapper around it. Us Neos of the binary world have always struggled with proper cipher methods to do so.

In other words, beginners always have had difficulty in implementing a reliable yet flexible method for securing cookies. I have used the following code often and I am sure this will help anyone looking for a quick-fix solution to secure cookies.

The basic architecture is to create static methods to which you pass a cookie and get a encrypted version in return. It also conveniently retrieves the Algorithm to use as well as the key to encrypt/decrypt from the web.config file and can be adapted to retrieve/generate the key in other ways.

Step 1: Start off with our class.
  1.  using System.Security.Cryptography; //and other...    
  2.      
  3.  public class Security    
  4.  {    
  5.      //If you dont want ur methods to be static then use this and  instantiate an object before use     
  6.      //byte[] Key;    
  7.      //string AlgorithmName;    
  8.      
  9.      public Security()    
  10.      {    
  11.          //If you dont want ur methods to be static then use this    
  12.          //char[] chars = { ',' };    
  13.          //string[] splits = ConfigurationManager.AppSettings["CookieSecurityKey"].Split(chars);    
  14.          //AlgorithmName = splits[0];    
  15.          //Key = new byte[Int32.Parse(splits[1])];// = KEY_64;    
  16.          //for (int i = 2; i < Key.Length + 2; i++)    
  17.          //  Key[i - 2] = byte.Parse(splits[i].Trim());             
  18.      }    
  19. ...    

You can store the key/algoritm name in the web.config like this:
  1. <appsettings>  
  2.     <!--First Item - algo name, second item - key size, third till end - key values-->  
  3.     <!--Possible Algorithms(valid key sizes) - DES(8), TripleDES(16,24), RC2(5,16), Rijndael(16,24,32)-->  
  4.     <!--Example: <add key ="CookieSecurityKey" value="RC2, 55, 2, 66, 23, 16, 118">-->  
  5.     <add key="CookieSecurityKey" value="DES, 8, 91, 161, 38, 16, 7, 24, 28, 222"></add>  
  6. </appsettings>  

Step 2: Create the basic methods that encode/decode a piece of string
  1. public static string Encode(string value, Byte[] key, string AlgorithmName)  
  2. {  
  3.  // Convert string data to byte array  
  4.  byte[] ClearData = System.Text.Encoding.UTF8.GetBytes(value);  
  5.   
  6.  // Now create the algorithm from the provided name  
  7.  SymmetricAlgorithm Algorithm = SymmetricAlgorithm.Create(AlgorithmName);  
  8.  Algorithm.Key = key; //you can apply your own key mechs here  
  9.  MemoryStream Target = new MemoryStream();  
  10.   
  11.  // Generate a random initialization vector (IV), helps to prevent brute-force  
  12.  Algorithm.GenerateIV();  
  13.  Target.Write(Algorithm.IV, 0, Algorithm.IV.Length);  
  14.   
  15.  // Encrypt information  
  16.  CryptoStream cs = new CryptoStream(Target, Algorithm.CreateEncryptor(), CryptoStreamMode.Write);  
  17.  cs.Write(ClearData, 0, ClearData.Length);  
  18.  cs.FlushFinalBlock();  
  19.   
  20.  // Convert the encrypted stream back to string  
  21.  return Convert.ToBase64String(Target.GetBuffer(), 0, (int)Target.Length);  
  22. }  
  23.   
  24. public static string Decode(string value, Byte[] key, string AlgorithmName)  
  25. {  
  26.  // Convert string data to byte array  
  27.  byte[] ClearData = Convert.FromBase64String(value);  
  28.   
  29.  // Create the algorithm  
  30.  SymmetricAlgorithm Algorithm = SymmetricAlgorithm.Create(AlgorithmName);  
  31.  Algorithm.Key = key;  
  32.  MemoryStream Target = new MemoryStream();  
  33.   
  34.  // Read IV and initialize the algorithm with it  
  35.  int ReadPos = 0;  
  36.  byte[] IV = new byte[Algorithm.IV.Length];  
  37.  Array.Copy(ClearData, IV, IV.Length);  
  38.  Algorithm.IV = IV;  
  39.  ReadPos += Algorithm.IV.Length;  
  40.   
  41.  // Decrypt information  
  42.  CryptoStream cs = new CryptoStream(Target, Algorithm.CreateDecryptor(), CryptoStreamMode.Write);  
  43.  cs.Write(ClearData, ReadPos, ClearData.Length - ReadPos);  
  44.  cs.FlushFinalBlock();  
  45.   
  46.  // Get the bytes from the memory stream and convert them to text  
  47.  return System.Text.Encoding.UTF8.GetString(Target.ToArray());  
  48. }  
Note: there methods are static so that you can use them directly from code without the additional line to create an object.

Step 3: Create the methods that encode/decode the cookie
  1. public static HttpCookie EncodeCookie(HttpCookie cookie)    
  2. {    
  3.     if (cookie == nullreturn null;    
  4.     
  5.     //get key and algorithm from web.config/appsettings    
  6.     //Comment this section if you do now what this method to be static    
  7.     char[] chars = { ',' };    
  8.     string[] splits = ConfigurationManager.AppSettings["CookieEncodingKey"].Split(chars);    
  9.     string AlgorithmName = splits[0];    
  10.     byte[] Key = new byte[Int32.Parse(splits[1])];// = KEY_64;    
  11.     for (int i = 2; i < Key.Length + 2; i++)    
  12.       Key[i - 2] = byte.Parse(splits[i].Trim());    
  13.     
  14.     HttpCookie eCookie = new HttpCookie(cookie.Name);    
  15.     
  16.     for (int i = 0; i < cookie.Values.Count; i++)    
  17.     {    
  18.         string value = HttpContext.Current.Server.UrlEncode(Encode(cookie.Values[i], Key, AlgorithmName));    
  19.         string name = HttpContext.Current.Server.UrlEncode(Encode(cookie.Values.GetKey(i), Key, AlgorithmName));  
  20.         eCookie.Values.Set(name, value);    
  21.     }    
  22.     return eCookie;    
  23. }    
  24.     
  25. public static HttpCookie DecodeCookie(HttpCookie cookie)    
  26. {    
  27.     if (cookie == nullreturn null;    
  28.     
  29.     //Comment this section if you do now what this method to be static    
  30.     char[] chars = { ',' };    
  31.     string[] splits = ConfigurationManager.AppSettings["CookieEncodingKey"].Split(chars);    
  32.     string AlgorithmName = splits[0];    
  33.     byte[] Key = new byte[Int32.Parse(splits[1])];// = KEY_64;    
  34.     for (int i = 2; i < Key.Length + 2; i++)    
  35.       Key[i - 2] = byte.Parse(splits[i].Trim());    
  36.     
  37.     HttpCookie dCookie = new HttpCookie(cookie.Name);    
  38.     
  39.     for (int i = 0; i < cookie.Values.Count; i++)    
  40.     {    
  41.         string value = Decode(HttpContext.Current.Server.UrlDecode(cookie.Values[i]), Key, AlgorithmName);    
  42.         string name = Decode(HttpContext.Current.Server.UrlDecode(cookie.Values.GetKey(i)), Key, AlgorithmName);    
  43.         dCookie.Values.Set(name, value);    
  44.     }    
  45.     return dCookie;    
  46. }  

At this point you have the basis setup and you can use the class in your code.

Encoding Example:
  1. HttpCookie cookie = new HttpCookie("MySecureCookie");  
  2. cookie["ID"] = "39369a0d-975e-4456-9ade-3f04639fd309"//recommended: use some unique value to later check for fraudulent cookies while retrieving  
  3. cookie["Name"] = username;  
  4. cookie["Email"] = email;  
  5.   
  6. cookie = Security.EncodeCookie(cookie); //encode  
  7. cookie.Expires = DateTime.Now.AddDays(90);  
  8. Response.Cookies.Add(cookie);  
  9. ...  

Decoding Example:
  1. HttpCookie cookie = context.Request.Cookies["MySecureCookie"];  
  2. cookie = Security.DecodeCookie(cookie); //decode  
  3.   
  4. if (cookie["ID"] == "39369a0d-975e-4456-9ade-3f04639fd309") cookieChecksOut = true;  
  5. //process cookie  
  6. ...  

This code allows using a single generic implementation to use a variety of algorithms based on their merits and properties. I hope this will help a lot of beginners.