Introduction
I am seeing a lot of questions people are asking on how to do encryption/decryption. To help those people, I have written a simple class incorporating several encryption/decryption functions:
byte[] Encrypt(byte[] clearData, byte[] Key, byte[] IV)
- encrypts a byte array with a key and an IV and returns a byte array;
string Encrypt(string clearText, string Password)
- encrypts a string with a password and returns a string;
byte[] Encrypt(byte[] clearData, string Password)
- encrypts a byte array with a password and returns a byte array;
void Encrypt(string fileIn, string fileOut, string Password)
- encrypts a file with a password and writes the encrypted bytes into another file.
For each of those, there is also a corresponding Decrypt
function. The Main
method is a simple testing method that exercises some of those functions. The 2nd and the 3rd Encrypt
functions call into the 1st function, so you will need to carry the 1st one around if you are using the 2nd or the 3rd. The last Encrypt
function (the one that works with files) is standalone. I made it operate in a stream-like manner, without reading the whole file into memory, which makes it possible to encrypt/decrypt gigabytes of data without going out of memory space.
I am using Rijndael algorithm in this sample. The reason for this is that it is 100% implemented in managed code in our libraries, so it does not rely on CryptoAPI or any encryption packs and will work everywhere. If you need performance, I would suggest replacing it with TripleDES (it is a one line change), and if you do, also do not forget to change the IV size to 8 bytes and the Key size to 16 bytes.
I have tried to document the code well, and I would like to encourage you to read through it and understand how it works, it should be pretty easy. You can also grab the whole thing, stick it into a .cs file and it should compile. If you run it, you will see it make some test encryption/decryption roundtrip; you can also provide a file name as a parameter, and it will encrypt the file into a <name>.encrypted file and then decrypt it back into a <name>.decrypted.
Enjoy!
P.S. A crypto-related FAQ can be found here and there is a good chapter on how to use crypto in "Writing Secure Code" by Michael Howard (2nd edition came out recently). For in depth information on crypto in general, "Applied Cryptography" by Bruce Schneier is an excellent resource.
using System;
using System.IO;
using System.Security.Cryptography;
public class EncDec
{
public static byte[] Encrypt(byte[] clearData, byte[] Key, byte[] IV)
{
MemoryStream ms = new MemoryStream();
Rijndael alg = Rijndael.Create();
alg.Key = Key;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms,
alg.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(clearData, 0, clearData.Length);
cs.Close();
byte[] encryptedData = ms.ToArray();
return encryptedData;
}
public static string Encrypt(string clearText, string Password)
{
byte[] clearBytes =
System.Text.Encoding.Unicode.GetBytes(clearText);
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
byte[] encryptedData = Encrypt(clearBytes,
pdb.GetBytes(32), pdb.GetBytes(16));
return Convert.ToBase64String(encryptedData);
}
public static byte[] Encrypt(byte[] clearData, string Password)
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
return Encrypt(clearData, pdb.GetBytes(32), pdb.GetBytes(16));
}
public static void Encrypt(string fileIn,
string fileOut, string Password)
{
FileStream fsIn = new FileStream(fileIn,
FileMode.Open, FileAccess.Read);
FileStream fsOut = new FileStream(fileOut,
FileMode.OpenOrCreate, FileAccess.Write);
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
Rijndael alg = Rijndael.Create();
alg.Key = pdb.GetBytes(32);
alg.IV = pdb.GetBytes(16);
CryptoStream cs = new CryptoStream(fsOut,
alg.CreateEncryptor(), CryptoStreamMode.Write);
int bufferLen = 4096;
byte[] buffer = new byte;
int bytesRead;
do {
bytesRead = fsIn.Read(buffer, 0, bufferLen);
cs.Write(buffer, 0, bytesRead);
} while(bytesRead != 0);
cs.Close();
fsIn.Close();
}
public static byte[] Decrypt(byte[] cipherData,
byte[] Key, byte[] IV)
{
MemoryStream ms = new MemoryStream();
Rijndael alg = Rijndael.Create();
alg.Key = Key;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms,
alg.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(cipherData, 0, cipherData.Length);
cs.Close();
byte[] decryptedData = ms.ToArray();
return decryptedData;
}
public static string Decrypt(string cipherText, string Password)
{
byte[] cipherBytes = Convert.FromBase64String(cipherText);
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65,
0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
byte[] decryptedData = Decrypt(cipherBytes,
pdb.GetBytes(32), pdb.GetBytes(16));
return System.Text.Encoding.Unicode.GetString(decryptedData);
}
public static byte[] Decrypt(byte[] cipherData, string Password)
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
return Decrypt(cipherData, pdb.GetBytes(32), pdb.GetBytes(16));
}
public static void Decrypt(string fileIn,
string fileOut, string Password)
{
FileStream fsIn = new FileStream(fileIn,
FileMode.Open, FileAccess.Read);
FileStream fsOut = new FileStream(fileOut,
FileMode.OpenOrCreate, FileAccess.Write);
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
Rijndael alg = Rijndael.Create();
alg.Key = pdb.GetBytes(32);
alg.IV = pdb.GetBytes(16);
CryptoStream cs = new CryptoStream(fsOut,
alg.CreateDecryptor(), CryptoStreamMode.Write);
int bufferLen = 4096;
byte[] buffer = new byte;
int bytesRead;
do {
bytesRead = fsIn.Read(buffer, 0, bufferLen);
cs.Write(buffer, 0, bytesRead);
} while(bytesRead != 0);
cs.Close();
fsIn.Close();
}
}
Need a Main
method to make this code complete? Write your own (it's quite simple) or visit this site, find this article there and grab it.
The encryption sample above had a very defined purpose - being extremely easy to read and understand. While it explains how to use symmetric encryption classes and gives some ideas on how to start implementing encryption in your applications, there are things you will have to do before it becomes a shippable piece of code. One of them I have already mentioned in the posting below - parameter checking and error handling. Check the parameters for being valid, wrap calls that can potentially fail into try
/catch
blocks, use finally
blocks to release resources (close files) if something goes wrong, etc.
Some cryptography specific considerations should also be there. For example, the salt values in PasswordDeriveBytes
should better be random rather than hard coded (sometimes it is ok to have them hard coded, for example, when encryption happens rarely and the code is not accessible by attackers). If the salt is random and changed frequently, you don't even have to keep it secret. Also, when possible, use byte[]
keys as opposed to passwords. Because of the human factor, password-based encryption is not the most secure way to protect information. In order to get 128bit of key information out of a password, it has to be long. If you are using just small letters that give you about 5 bits of information per character and your password will have to be over 25 characters long to get to 128bit. If you are using capital letters and some symbols you can get to about 7 bits per character and your password minimum length will have to be around 18 characters (how long is your password? ;-)).
I also invite you to read my .NET-security centric blog.