.Net Encryption simple AES wrapper
by Blue Beetle
- Published:July 29th, 2008
- Comments:10 Comments
- Category:.NET 2.0, .NET 3.5, Security, Utilities
So, I've been hoping to make a nice little generic encryption module that essentially just wraps the .Net System stuff. Why? 'Cause I'd like to move some data around with reasonable security (it doesn't have to be completely hacker-proof, just reasonably obscured -- the data I'm covering isn't worth that much, but it's worth more than zero so it deserves some due process.) This is done with that atitude, if you want hard-core encryption, then your better off not using a wrapper (set your .IV cleverly, don't resize improper-length keys, etc.) Alternatively, use an existing, clean, third party app (or write your own -- R4 is not too terrible to code and is very powerful, especially if you tweak it during the implementation).
Here, I wanted quick, easy, and portable. I've read .Net makes things simple -- didn't really feel that way, so I tried to wrap one of the stronger algorithims, the AesCryptoServiceProvider, into something simple to call. There are a couple of things to say before the code drops: Microsoft mediates all it's encryption with a "machinekey" -- this can be found in all sorts of places depending on which OS you are using -- I think a common place to find it might be in the IIS configs. I haven't spent time dealing with this, but it will bite you if you use a web farm -- to use encrpytion across the farm, you're gonna need to sync your machinekeys or you'll only be able to decrypt data which has been encrypted on the same machine (fine for most apps, woe on a webfarm). I had a couple of false steps with this code, here are the caveats: this builds in 3.5, so you'll have trouble building or running it on anything older than Windows XP (you can run it on Win2000, but that's another article). There seems to be a common practice of scanning an encrypted byte array buffer and cutting it at the first zero byte -- this will often work, but also fail occasionally enough to drive you nuts -- errors will kick from the StreamReader's ReadToEnd method -- instead use .ToArray (as below) on the memorystream to avoid accidentally truncating your encrypted data.
OK, so, here is the code -- hopefully the formatting is tolerable, I'll have to trim it up a bit, so, sorry if you run into trouble with that (feel free to yell in the comments).
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography; namespace AesWrapper { public class Encryptor { /// /// Encrypts the sourceString, returns this result as an Aes encrypted, BASE64 encoded string /// /// a plain, Framework string (ASCII, null terminated) /// returns an Aes encrypted, BASE64 encoded string public static string EncryptString(string plainSourceStringToEncrypt, byte[] key) { //Set up the encryption objects AesCryptoServiceProvider acsp = Encryptor.GetProvider(key); byte[] sourceBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(plainSourceStringToEncrypt); ICryptoTransform ictE = acsp.CreateEncryptor(); //Set up stream to contain the encryption System.IO.MemoryStream msS = new System.IO.MemoryStream(); //Perform the encrpytion, storing output into the stream CryptoStream csS = new CryptoStream(msS, ictE, CryptoStreamMode.Write); csS.Write(sourceBytes, 0, sourceBytes.Length); csS.FlushFinalBlock(); //sourceBytes are now encrypted as an array of secure bytes byte[] encryptedBytes = msS.ToArray(); //.ToArray() is important, don't mess with the buffer //return the encrypted bytes as a BASE64 encoded string return System.Convert.ToBase64String(encryptedBytes); } /// /// Decrypts a BASE64 encoded string of encrypted data, returns a plain string /// /// an Aes encrypted AND base64 encoded string /// any byte array (16 bytes long is optimal) /// returns a plain string public static string DecryptString(string base64StringToDecrypt, byte[] key) { //Set up the encryption objects AesCryptoServiceProvider acsp = Encryptor.GetProvider(key); byte[] RawBytes = System.Convert.FromBase64String(base64StringToDecrypt); ICryptoTransform ictD = acsp.CreateDecryptor(); //RawBytes now contains original byte array, still in Encrypted state //Decrypt into stream System.IO.MemoryStream msD = new System.IO.MemoryStream(RawBytes, 0, RawBytes.Length); CryptoStream csD = new CryptoStream(msD, ictD, CryptoStreamMode.Read); //csD now contains original byte array, fully decrypted //return the content of msD as a regular string return (new System.IO.StreamReader(csD)).ReadToEnd(); } private static AesCryptoServiceProvider GetProvider(byte[] key) { //Set up the encryption objects AesCryptoServiceProvider result = new AesCryptoServiceProvider(); byte[] RealKey = Encryptor.GetKey(key, result); result.Key = RealKey; result.IV = RealKey; return result; } private static byte[] GetKey(byte[] suggestedKey, AesCryptoServiceProvider p) { byte[] kRaw = suggestedKey; List<byte> kList = new List</byte><byte>; for (int i = 0; i < p.LegalKeySizes[0].MinSize/8; i++ ) { kList.Add(kRaw[i % kRaw.Length]); } byte[] k = kList.ToArray(); return k; } } }
Ok, that's that. The methods are all static, so, after you add a reference to the class, you can call it like this (testSource is any typical source string, button3 is just a button on my test form):
private void button3_Click(object sender, EventArgs e) { byte[] kRaw = { 2, 3, 5, 7, 11, 17 }; //whatever key you want MessageBox.Show(testSource); //raw source string enc = AesWrapper.Encryptor.EncryptString(testSource, kRaw); MessageBox.Show(enc); //ecrypted MessageBox.Show(AesWrapper.Encryptor.DecryptString(enc,kRaw)); //decrypted }


10 Comments
Dices do not mices make. Beware: The rodent dentist lurks. I have counted all the yellow flowers.
Why was everything in spanish or italian earlier…
The miniBits theme was developed by an Italian company.
You have a serious bug in the GetKey function. Instead of:
it should be:
Otherwise keying material of length 8 will only use the first letter.
I would also point out that while padding up to the full key length is a good idea, second guessing what the key should be can lead to incompatibilities.
And generating and prepending a random key would be a lot more secure.
Got error message:Error 5 The type or namespace name \’AesCryptoServiceProvider\’ could not be found (are you missing a using directive or an assembly reference?)
Could you help me ?
Thanks
Michael Yang
I fixed above error message. thanks. But what\’s i <?
Thanks
Michael
I\’ve tried viewing this page with IE6, FireFox 3, Opera 9 and Chrome. The code is partially obscured by the links on the right in every case.
Michael, < is the less than sign; It should display the sign correctly now.
There was definitely a problem in GetKey, p.LegalKeySizes[0].MinSize returns a number of bits, and what’s needed is a number of bytes, so, I’ve fixed this in the for loop boundaries and count conditions (it should also be a bit more readable). Thanks for seeing that bug, Michael.
the lt; and gt; things are html constants not C#, they will sometimes appear if a less-than or greater-than symbol gets improperly translated by the blog — those are display issues and I hope those are fixed.
There are a number of suggestions already in the article about how to make things stronger. The whole idea here (q.v. the title) is a Simple Wrapper. I’d recommend (again, as above) anybody who wants to use Aes to its full strength should not use a wrapper but should use the native .Net objects directly.
Hardcoding the key (as I did in the button click) is clearly a point of vulnerability (typically the calling app should acquire encryption keys from a trusted source via protected delivery), the key is explicit here for clarity, not security.
There is an infinite regression of how to make a given encryption stronger. A simple example is how the DES encryption, once it became trivially crackable, morphed into the triple-DES algorithm (basically a hack — just keep a-cryptin’ it.)
Please feel free to suggest stronger GetKey methods, password delivery modules, etc. in the comments.
-BlueBeetle
I’ve enforced some wrapping on the code blocks so, hopefully, thing’s won’t get clipped in the browser.
You may still end up with artifacts (like extra line breaks, html constants, etc.) if you cut-and-paste. There are just so many apps to paste from and so many to paste into that there is no cureall. Still, hopefully the highlighting, etc. will help and, remember, if you see ampersands (&), SGML tags, etc. in your paste, they’re html translations of C# (almost never valid C# themselves).