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
Note: there methods are static so that you can use them directly from code without the additional line to create an object.
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());
}
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.