C#: String [Dictionary] encryption with cryptography
In last post, we have seen how we can leverage ASP.NET MVC framework extensibility to handle encrypted query string. After reading that post one reader asked how to encrypt/decrypt query string (i.e. dictionary) in ASP.NET. We had some discussion on post comment and found that after encrypting query string user run in another error "The request filtering module is configured to deny a request that contains a double escape sequence" while using encrypted string as route value. So I thought to write post on string encryption/decryption which discuss precautions before using it as part of URL. However I cant post actual implementation here but we will look in other solution. (I am not much sound in cryptography still we will try to learn together :).)
Encryption, Cryptography & string
All cryptographic operation is done on byte and not on string so we need to create byte array from string. And in case of encrypted query string, we need to create string from query string dictionary and I recommend to apply some cryptography operation on string rather than just applying plus/minus or similar operation on character.
String Encryption
So here we will try to understand string encryption with code snippet.
1. Create string from dictionary
Following code snippet show how to create string from dictionary.
static string StringFromDictionary(Dictionary<string, string> dictionary)
{
return string.Join("-", dictionary.Select(d => d.Key + "+" + d.Value));
}
2. Convert string in byte array
As we noted earlier, all cryptographic operation are done on byte and not on string so we need to create byte array from string.
byte[] queryString = Encoding.UTF8.GetBytes(StringFromDictionary(dictionary));
3. Apply encryption on byte array
Now we have byte array ready so we can apply cryptographic operation on it. For this post we are using .NET framework RijndaelManaged class.
// Change keyBytes or generate based on some logic
// We are using IV (Initialization Vector) same as keyBytes
// but strongly recommend to change or generate separate one
byte[] keyBytes = new byte[16] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
RijndaelManaged rijndaelManaged = new RijndaelManaged
{
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
KeySize = 128,
BlockSize = 128,
Key = keyBytes,
IV = keyBytes
};
Here we have initialized RijndaelManaged instance and now we can use it for encryption and decryption. For demonstrate purpose, I am using same IV (Initialization Vector) as private key but it is strongly recommend to generate separate one.
static byte[] EncryptBytes(byte[] bytes)
{
return rijndaelManaged.CreateEncryptor().TransformFinalBlock(bytes, 0, bytes.Length);
}
And we can retrieve encrypted byte as
byte[] encryptedBytes = EncryptBytes(queryString);
4. Convert encrypted byte array in Base64 string
Now we have encrypted byte array and we need to convert it in string, but as it is encrypted byte array we are converting it in Base64 format.
static string ToBase64(byte[] bytes)
{
string strBase64;
strBase64 = Convert.ToBase64String(bytes);
return strBase64.Replace('+', '-').Replace('/', '_').Replace('=', ',');
}
After converting it in Base64 we are replacing +/= with -_, because +/= is the only symbol which is part of Base64 character set and it could change the meaning of URL or it could return in error "The request filtering module is configured to deny a request that contains a double escape sequence". So we are replacing it with -_, and before decryption we will replace it back to +/=.
String Decryption
Decryption step is simply reverse of above. Let’s have a quick view of it.
1. Convert encrypted Base64 string in byte array
static byte[] FromBase64(string encryptedText)
{
encryptedText = encryptedText.Replace('-', '+').Replace('_', '/').Replace(',', '=');
return Convert.FromBase64String(encryptedText);
}
byte[] encryptedBytes = FromBase64(encryptedString);
2. Apply decryption on byte array
Once we have encrypted byte array back we can decrypt it with RijndaelManaged.
static byte[] DecryptBytes(byte[] bytes)
{
return rijndaelManaged.CreateDecryptor().TransformFinalBlock(bytes, 0, bytes.Length);
}
byte[] decryptedBytes = DecryptBytes(encryptedBytes);
3. Populated dictionary from decrypted bytes
Now we can populate dictionary back from decrypted byte array as explained below.
static Dictionary<string, string> DictionaryFromBytes(byte[] bytes)
{
string decryptedString = Encoding.UTF8.GetString(bytes);
Dictionary<string, string> dictionary = new Dictionary<string, string>();
string[] keyValuePair = decryptedString.Split('-');
foreach (string key in keyValuePair)
{
string[] keyValue = key.Split('+');
dictionary.Add(keyValue[0], keyValue[1]);
}
return dictionary;
}
Conclusion
Here we have discussed how to encrypt string with cryptographic operation and what are the required precautions while using Base64 encoded string in URL. Along with this don’t forget to read how to implement encrypted query string in ASP.NET MVC application with lesser code.
You can follow me on twitter for latest link and update on ASP.NET & MVC.