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.

using System.Security.Cryptography; //and other...

public class Security
{
//If you dont want ur methods to be static then use this and instantiate an object before use
//byte[] Key;
//string AlgorithmName;

public Security()
{
//If you dont want ur methods to be static then use this
//char[] chars = { ',' };
//string[] splits = ConfigurationManager.AppSettings["CookieSecurityKey"].Split(chars);
//AlgorithmName = splits[0];
//Key = new byte[Int32.Parse(splits[1])];// = KEY_64;
//for (int i = 2; i < Key.Length + 2; i++)
// Key[i - 2] = byte.Parse(splits[i].Trim());
}
...

You can store the key/algoritm name in the web.config like this:








Step 2: Create the basic methods that encode/decode a piece of string

public static string Encode(string value, Byte[] key, string AlgorithmName)
{
// Convert string data to byte array
byte[] ClearData = System.Text.Encoding.UTF8.GetBytes(value);

// Now create the algorithm from the provided name
SymmetricAlgorithm Algorithm = SymmetricAlgorithm.Create(AlgorithmName);
Algorithm.Key = key; //you can apply your own key mechs here
MemoryStream Target = new MemoryStream();

// Generate a random initialization vector (IV), helps to prevent brute-force
Algorithm.GenerateIV();
Target.Write(Algorithm.IV, 0, Algorithm.IV.Length);

// Encrypt information
CryptoStream cs = new CryptoStream(Target, Algorithm.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(ClearData, 0, ClearData.Length);
cs.FlushFinalBlock();

// Convert the encrypted stream back to string
return Convert.ToBase64String(Target.GetBuffer(), 0, (int)Target.Length);
}

public static string Decode(string value, Byte[] key, string AlgorithmName)
{
// Convert string data to byte array
byte[] ClearData = Convert.FromBase64String(value);

// Create the algorithm
SymmetricAlgorithm Algorithm = SymmetricAlgorithm.Create(AlgorithmName);
Algorithm.Key = key;
MemoryStream Target = new MemoryStream();

// Read IV and initialize the algorithm with it
int ReadPos = 0;
byte[] IV = new byte[Algorithm.IV.Length];
Array.Copy(ClearData, IV, IV.Length);
Algorithm.IV = IV;
ReadPos += Algorithm.IV.Length;

// Decrypt information
CryptoStream cs = new CryptoStream(Target, Algorithm.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(ClearData, ReadPos, ClearData.Length - ReadPos);
cs.FlushFinalBlock();

// Get the bytes from the memory stream and convert them to text
return System.Text.Encoding.UTF8.GetString(Target.ToArray());
}
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

public static HttpCookie EncodeCookie(HttpCookie cookie)
{
if (cookie == null) return null;

//get key and algorithm from web.config/appsettings
//Comment this section if you do now what this method to be static
char[] chars = { ',' };
string[] splits = ConfigurationManager.AppSettings["CookieEncodingKey"].Split(chars);
string AlgorithmName = splits[0];
byte[] Key = new byte[Int32.Parse(splits[1])];// = KEY_64;
for (int i = 2; i < Key.Length + 2; i++)
Key[i - 2] = byte.Parse(splits[i].Trim());

HttpCookie eCookie = new HttpCookie(cookie.Name);

for (int i = 0; i < cookie.Values.Count; i++)
{
string value = HttpContext.Current.Server.UrlEncode(Encode(cookie.Values[i], Key, AlgorithmName));
string name = HttpContext.Current.Server.UrlEncode(Encode(cookie.Values.GetKey(i), Key, AlgorithmName));
eCookie.Values.Set(name, value);
}
return eCookie;
}

public static HttpCookie DecodeCookie(HttpCookie cookie)
{
if (cookie == null) return null;

//Comment this section if you do now what this method to be static
char[] chars = { ',' };
string[] splits = ConfigurationManager.AppSettings["CookieEncodingKey"].Split(chars);
string AlgorithmName = splits[0];
byte[] Key = new byte[Int32.Parse(splits[1])];// = KEY_64;
for (int i = 2; i < Key.Length + 2; i++)
Key[i - 2] = byte.Parse(splits[i].Trim());

HttpCookie dCookie = new HttpCookie(cookie.Name);

for (int i = 0; i < cookie.Values.Count; i++)
{
string value = Decode(HttpContext.Current.Server.UrlDecode(cookie.Values[i]), Key, AlgorithmName);
string name = Decode(HttpContext.Current.Server.UrlDecode(cookie.Values.GetKey(i)), Key, AlgorithmName);
dCookie.Values.Set(name, value);
}
return dCookie;
}

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

Encoding Example:

HttpCookie cookie = new HttpCookie("MySecureCookie");
cookie["ID"] = "39369a0d-975e-4456-9ade-3f04639fd309"; //recommended: use some unique value to later check for fraudulent cookies while retrieving
cookie["Name"] = username;
cookie["Email"] = email;

cookie = Security.EncodeCookie(cookie); //encode
cookie.Expires = DateTime.Now.AddDays(90);
Response.Cookies.Add(cookie);
...

Decoding Example:

HttpCookie cookie = context.Request.Cookies["MySecureCookie"];
cookie = Security.DecodeCookie(cookie); //decode

if (cookie["ID"] == "39369a0d-975e-4456-9ade-3f04639fd309") cookieChecksOut = true;
//process cookie
...

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.