Symmetric encryption algorithms use one key for encrypting and decrypting data. You can create a utility class that performs the encryption and decryption of sensitive data:
public static class SymmetricEncryptionUtility
{
private static bool _ProtectKey;
private static string _AlgorithmName;
// You can use this property to specify the name of the algorithm (DES, TripleDES, Rijndael and RC2 )
public static string AlgorithmName
{
get { return _AlgorithmName; }
set { _AlgorithmName = value; }
}
// You can use this property to specify whether the key should be protected through the DPAPI.
public static bool ProtectKey
{
get { return _ProtectKey; }
set { _ProtectKey = value; }
}
public static void GenerateKey(string targetFile) { }
public static void ReadKey(SymmetricAlgorithm algorithm, string file) { }
public static byte[] EncryptData(string data, string keyFile) { }
public static string DecryptData(byte[] data, string keyFile) { }
}
Because the class is a utility class with static members only, you can make it a static class. To use this class, you must:
1. Set the algorithm name.
2. Generate a key if none exists.
3. Call the EncryptData and DecryptData methods, which internally call the ReadKey method for initializing the algorithm.
You can generate encryption keys through the algorithm classes and the GenerateKey() method looks like this:
public static void GenerateKey(string targetFile)
{
// Create the algorithm
SymmetricAlgorithm Algorithm = SymmetricAlgorithm.Create(AlgorithmName);
Algorithm.GenerateKey();
// Get the key
byte[] Key = Algorithm.Key;
if (ProtectKey)
{
// Use DPAPI to encrypt key
Key = ProtectedData.Protect(Key, null, DataProtectionScope.LocalMachine);
}
// Store the key in a file called key.config
using (FileStream fs = new FileStream(targetFile, FileMode.Create))
{
fs.Write(Key, 0, Key.Length);
}
}
The ReadKey method reads the key from the file created by the GenerateKey method, as follows:
public static void ReadKey(SymmetricAlgorithm algorithm, string keyFile)
{
byte[] Key;
using (FileStream fs = new FileStream(keyFile, FileMode.Open))
{
Key = new byte[fs.Length];
fs.Read(Key, 0, (int)fs.Length);
}
if (ProtectKey)
algorithm.Key = ProtectedData.Unprotect(Key, null, DataProtectionScope.LocalMachine);
else
algorithm.Key = Key;
}
You can use EncryptData method to encrypt a string to a byte array.
public static byte[] EncryptData(string data, string keyFile)
{
// Convert string data to byte array, because all the encryption functions of the algorithms
// require byte arrays as input parameters, by using Encoding class of System.Text namespace
byte[] ClearData = System.Text.Encoding.UTF8.GetBytes(data);
// Create the algorithm according to the AlgorithmName property of the class.
// This value can be one of the names RC2, Rijndael, DES, or TripleDES.
// The factory method of the SymmetricAlgorithm creates the appropriate instance, while you can register
// additional cryptography classes through the <cryptographySettings> section in the machine.config file.
SymmetricAlgorithm Algorithm = SymmetricAlgorithm.Create(AlgorithmName);
ReadKey(Algorithm, keyFile);
// Encrypt information. In this case you use memory stream as the target of your encryption operation
MemoryStream Target = new MemoryStream();
// Generate a random initialization vector (IV) to use for the algorithm
Algorithm.GenerateIV();
Target.Write(Algorithm.IV, 0, Algorithm.IV.Length);
// Encrypt actual data
CryptoStream cs = new CryptoStream(Target, Algorithm.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(ClearData, 0, ClearData.Length);
cs.FlushFinalBlock();
// Return the encrypted stream of data as a byte array
return Target.ToArray();
}
Important note:
In the code above GenerateIV method is used. If your application exchanges the same information multiple times between a client and a server, simple encryption will always result in the same encrypted representation of the information. This makes brute-force attacks easier. To add some sort of random information, symmetric algorithms support IV. These IVs are not only added to the encrypted stream of bytes themselves but are also used as input for encrypting the first block of data. When you are using the CryptoStream for encrypting information, you should call the FlushFinalBlock method to make sure that the last block of encrypted data is written appropriately to the target. You have to add the IV itself to the encrypted set of bytes because you need the information later to be able to decrypt the encrypted content entirely.
You can use DecryptData method to decrypt a byte array to a string:
public static string DecryptData(byte[] data, string keyFile)
{
// Create the algorithm
SymmetricAlgorithm Algorithm = SymmetricAlgorithm.Create(AlgorithmName);
ReadKey(Algorithm, keyFile);
// Decrypt information
MemoryStream Target = new MemoryStream();
// Read IV and initialize the algorithm with it
int ReadPos = 0;
byte[] IV = new byte[Algorithm.IV.Length];
Array.Copy(data, IV, IV.Length);
Algorithm.IV = IV;
ReadPos += Algorithm.IV.Length;
CryptoStream cs = new CryptoStream(Target,Algorithm.CreateDecryptor(), CryptoStreamMode.Write);
cs.Write(data, ReadPos, data.Length – ReadPos);
cs.FlushFinalBlock();
// Get the bytes from the memory stream and convert them to text
return Encoding.UTF8.GetString(Target.ToArray());
}