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
}
This stuff can get complicated enough that it is nice to have a wrapper that lets you not have to cogitate overmuch each time you want to transmit data with some security. This is just the way I did it, there are surely many others. It's testing well for me so far, but, as always, I'm sharing this with the (hopefully not vain) hope of helping -- use as you see fit and sorry if it gives you any grief -- no warrantees or claims are attached.
Happy encrypting! Be well.
--BlueBeetle

Rss Comment

10 Comments

  1. Dices do not mices make. Beware: The rodent dentist lurks. I have counted all the yellow flowers.

    #1 Alex
  2. Why was everything in spanish or italian earlier…

    #2 bogdan
  3. The miniBits theme was developed by an Italian company.

    #3 Alex
  4. You have a serious bug in the GetKey function. Instead of:

    kList.Add(kRaw[i % kRaw.Length]);

    it should be:

    kList.Add(kRaw[(i / 8 ) % kRaw.Length]);

    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.

    #4 Johnny casey
  5. 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

    #5 Michael Yang
  6. I fixed above error message. thanks. But what\’s i <?

    Thanks
    Michael

    #6 Michael Yang
  7. 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.

    #7 Gary Walker
  8. Michael, &lt; is the less than sign; It should display the sign correctly now.

    #8 Bogdan Varlamov (a.k.a. Phantom Stranger)
  9. 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

    #9 Blue Beetle
  10. 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).

    #10 Blue Beetle

Post a Comment





This is a captcha-picture. It is used to prevent mass-access by robots. (see: www.captcha.net)

You must read and type the 5 chars within 0..9 and A..F, and submit the form.

  

Oh no, I cannot read this. Please, generate a