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.

6 comments:

  1. Hi. Can I use this code in a commercial product? Thanks.

    ReplyDelete
  2. Yes you can. if possible, mention my name somewhere in credits

    ReplyDelete
  3. its not working, throwing exception "invalid value for base64 string"

    ReplyDelete
  4. Well you will have to try to post a bit more details on how you are using it.

    One probably cause is that you are not reading the right cookie or it is being updated/changed from another part of your code without encryption.

    ReplyDelete
  5. It not working if not change "CookieSecurityKey" to "CookieEncodingKey" in example. After it works great. Thanks so much.

    ReplyDelete
  6. Getting a null reference error on the line included, not sure why? it is passing the key, and I don't create another instance of "Algorithm'. All code is as written from this site.

    " Algorithm.Key = key; "

    MSVS 2008, Framework 3.5SP1

    ReplyDelete