Advanced Encryption Standard (AES) also called the Rijndael cipher, based off a combination of the two Belgian author's names Joan Daemen and Vincent Rijmen. AES was accepted in 2002 to replace DES cipher and quickly became the preferred encryption choice to hide secret data. AES is a symmetric cipher, also known as shared key cipher, which means that it uses one single key for both encryption and decryption.
In .Net the AES is visible through the System.Security.Cryptography namespace.
I use the RijndaelManaged class in this example to show you how to easily take a object, serialize it to an XML string, encode it to a byte array, and then deserialize it back to a new copy of the original object.
This example originates from a recent project of mine where I needed to securely store a user object containing sensitive information. This way I can quickly load and save my serialized object to disk and have it encrypted using my secret key.
To show you the different steps, I created a Console App that goes through the different steps:
class Program
{
static void Main(string[] args)
{
const string SecretKey = "needstobe32bytesneedstobe32bytes"; //need to be 32 bytes for 256 bit encryption
var oldUser = new User() {Password = "mysecret", Username = "MyName"};
var str = Serializer.SerializeToString(oldUser); //Serialize User to XML string
Console.WriteLine("Serialized object: "+str);
User newUser = Serializer.FromString<User>(str); //Deserialize User from XML string
Console.WriteLine("****");
Console.WriteLine("Old user name is: "+oldUser.Username+" and new user name is: "+newUser.Username);
Console.ReadKey();
byte[] encryptedOldUser = Security.Encrypt(str, SecretKey); //Encrypt XML string using AES
Console.WriteLine("Encrypted old user: "+Encoding.ASCII.GetString(encryptedOldUser));
Console.WriteLine("****");
string decryptedOldUser = Security.Decrypt(encryptedOldUser, SecretKey); //Decrypt bytes
Console.WriteLine("decrypted old user: " + decryptedOldUser);
User secureUser = Serializer.FromString<User>(decryptedOldUser); //Serialize back to a new User object
Console.WriteLine("****");
Console.WriteLine("Old user name is: " + oldUser.Username + " and secure user name is: " + secureUser.Username);
Console.ReadKey();
}
}
It uses two three classes, one User class that is our guinea pig for the experiment, one Serializer helper class, and one Encryption class.
This serializer class is pretty self-explanatory, and has two methods to go from object to XML and back. One note though is the generics in the FromString method, which saves us from doing the casting later on.
public class Serializer
{
public static string SerializeToString(object obj)
{
XmlSerializer s = new XmlSerializer(obj.GetType());
TextWriter w = new StringWriter();
s.Serialize(w, obj);
return w.ToString();
}
public static T FromString<T>(string str)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
var stringReader = new StringReader(str);
XmlTextReader xmlReader = new XmlTextReader(stringReader);
return (T)xs.Deserialize(xmlReader);
}
}
The Security class is an abstraction class to make the encryption and decryption a little smoother. It contains the obvious encrypt/decrypt methods and some underlying methods to help us with our cipher key and initialization vector. Most of the main code here is taken from a Microsoft AES example found here: http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged.aspx.
I thought it needed a bit more polish to be easier to use with your own key and a static IV, as well as using it along with serialization.
public class Security
{
public static byte[] Encrypt(string text, string key)
{
return EncryptStringToBytesAes(text, GetKeyFromString(key), GetIV());
}
public static string Decrypt(byte[] data, string key)
{
return DecryptStringFromBytesAes(data, GetKeyFromString(key), GetIV());
}
private static byte[] EncryptStringToBytesAes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
// Declare the stream used to encrypt to an in memory
// array of bytes.
MemoryStream msEncrypt = null;
// Declare the RijndaelManaged object
// used to encrypt the data.
RijndaelManaged aesAlg = null;
try
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
msEncrypt = new MemoryStream();
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
// Return the encrypted bytes from the memory stream.
return msEncrypt.ToArray();
}
private static string DecryptStringFromBytesAes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("IV");
// Declare the RijndaelManaged object
// used to decrypt the data.
RijndaelManaged aesAlg = null;
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
try
{
// Create a RijndaelManaged object
// with the specified key and IV.
aesAlg = new RijndaelManaged();
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
finally
{
// Clear the RijndaelManaged object.
if (aesAlg != null)
aesAlg.Clear();
}
return plaintext;
}
private static byte[] GetIV()
{
byte[] iv = { 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8 };
return iv;
}
private static byte[] GetKeyFromString(string key)
{
RijndaelManaged myRijndael = new RijndaelManaged();
ASCIIEncoding encoding = new ASCIIEncoding();
myRijndael.Key = encoding.GetBytes(key);
return myRijndael.Key;
}
}
And finally our little User class:
[Serializable]
public class User
{
public string Username { get; set; }
public string Password { get; set; }
}
The next step would be to store the encrypted data to disk or in a database, but I’ll leave that for another blog post! Hope this code can help you to quickly implement serialization and encryption! ☺