Tamper Proof Query String (written in C#)
Original CodeProject article - http://www.codeproject.com/aspnet/TamperProofQueryString.asp
This is the few changes that I did to the Helper function to have this working
static public string encode(string value){ return System.Web.HttpUtility.UrlEncode(TamperProofStringEncode(value, System.Configuration.ConfigurationManager.AppSettings["TamperProofKey"]).Replace('+', '@'));}
static public string decode(string value)
{
return TamperProofStringDecode(System.Web.HttpUtility.UrlDecode(value).Replace('@', '+'), System.Configuration.ConfigurationManager.AppSettings["TamperProofKey"]);
}
The URLDecode()
method of System.Web.HttpUtility
does not behave in an intuitive fashion when called repeatedly. For example: calling UrlEncode()
with the string “abc+=” results in “abc%2b%3d”. The first call to URLDecode()
with “abc%2b%3d” correctly results in “abc+=”. Calling UrlDecode()
again with “abc+=” results in “abc =” - the ‘+’ was replaced with a space. Why is this a problem at all? This is a problem because some .NET Framework methods silently call URLDecode()
. My testing shows that the results from Response.QueryString[]
is URL-decoded even though nothing is mentioned in the documentation. To avoid this problem, I recommend replacing all the ‘+’ characters with ‘@’ in the Base64 output string. The ‘@’ character is URL safe and does not change when multiple calls are made to UrlDecode()
.
using System;
namespace Common.SharedClass
{
public class TamperProofQueryString
{
#region TamperProofStringEncode
static public string TamperProofStringEncode(string value, string key)
{
System.Security.Cryptography.MACTripleDES mac3des = new System.Security.Cryptography.MACTripleDES();
System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
mac3des.Key = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(key));
return System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(value)) + System.Convert.ToChar("-") + System.Convert.ToBase64String(mac3des.ComputeHash(System.Text.Encoding.UTF8.GetBytes(value)));
}
#endregion
#region TamperProofStringDecode
//Function to decode the string
//Throws an exception if the data is corrupt
static public string TamperProofStringDecode(string value, string key)
{
String dataValue = string.Empty;
String calcHash = string.Empty;
String storedHash = string.Empty;
System.Security.Cryptography.MACTripleDES mac3des = new System.Security.Cryptography.MACTripleDES();
System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
mac3des.Key = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(key));
try
{
dataValue = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(value.Split(System.Convert.ToChar("-"))[0]));
storedHash = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(value.Split(System.Convert.ToChar("-"))[1]));
calcHash = System.Text.Encoding.UTF8.GetString(mac3des.ComputeHash(System.Text.Encoding.UTF8.GetBytes(dataValue)));
if (storedHash != calcHash)
{
//Data was corrupted
throw new ArgumentException("Hash value does not match");
//This error is immediately caught below
}
}
catch (System.Exception)
{
throw new ArgumentException("Invalid TamperProofString");
}
return dataValue;
}
#endregion
#region encode
static public string encode(string value)
{
return System.Web.HttpUtility.UrlEncode(TamperProofStringEncode(value, System.Configuration.ConfigurationManager.AppSettings["TamperProofKey"]).Replace('+', '@'));
}
#endregion
#region decode
static public string decode(string value)
{
return TamperProofStringDecode(System.Web.HttpUtility.UrlDecode(value).Replace('@', '+'), System.Configuration.ConfigurationManager.AppSettings["TamperProofKey"]);
}
#endregion
}
}