Symmetric Encryption in C#

Whenever I find myself needing to use a symmetric encryption algorithm, I always seem to write more or less the same code substituting whichever built in .NET cryptography class I need to use. So I decided to write a generic encrypt/decrypt cipher utility that allows you to specify whichever symmetric encryption algorithm you wish to use.

.NET Symmetric Encryption Algorithms

Before I launch into the code, here's a quick summary of some of the symmetric encryption algorithms provided by .NET.

AES (Advanced Encryption Standard)

Created in 2001, it is a variant of Rijndael. It offers all round good performance and a good level of security. A fully managed implementation is provided by the AesManaged class. The AesCryptoServiceProvider class provides a faster implementation calling into native code.

Rijndael

Rijndael is pretty much the same algorithm as AES in that AES was born from the Rijndael algorithm with some tweaks to the allowed block and key sizes. You should use AES over Rijndael, but you've got to love the name Rijndael! A fully managed implementation is provided by the class RijndaelManaged.

Triple DES (Data Encryption Standard)

Basically, this is the DES algorithm applied three times to remove some known flaws in the DES cipher. The TripleDESCryptoServiceProvider class provides a native implementation.

Which Symmetric Encryption Algorithm Should You Use?

I would personally use AesManaged as it doesn't rely on native code and offers good performance and security. It is worth noting that symmetric encryption isn't the best means of encryption so you should be careful if your data is extra sensitive. For much better security you should look at asymmetric encryption, also called public-private key encryption.

Why is it Called Symmetric Encryption?

It's called symmetric encryption because such algorithms use the same key to encrypt data as they do to decrypt data.

AES Example

The cipher utility accepts a generic parameter. Use AesManaged for AES encryption:

string plain = "Something you want to keep private with AES";

string encrypted = CipherUtility.Encrypt<AesManaged>(plain, "password", "salt");

string decrypted = CipherUtility.Decrypt<AesManaged>(encrypted, "password", "salt");
Triple DES Example

At the moment, there's no managed version of Triple DES built into the .NET library, so you'll have to use the TripleDESCryptoServiceProvider with the cipher utility:

string plain = "Something you want to keep private with Triple DES";

string encrypted = CipherUtility.Encrypt<TripleDESCryptoServiceProvider>(plain, "password", "salt");

string decrypted = CipherUtility.Decrypt<TripleDESCryptoServiceProvider>(encrypted, "password", "salt");

Rijndael Example

Definitely the symmetric encryption algorithm with the best name, use RijndaelManaged to leverage it:

string plain = "Something you want to keep private with Rijndael";

string encrypted = CipherUtility.Encrypt<RijndaelManaged>(plain, "password", "salt");

string decrypted = CipherUtility.Decrypt<RijndaelManaged>(encrypted, "password", "salt");

The C# Symmetric Encryption Cipher Utility

All the above examples rely on the following cipher utility written in C# which provides generic encrypt and decrypt methods for all the concrete symmetric encryption algorithms in .NET.

using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;

public class CipherUtility
{
   public static string Encrypt<T>(string value, string password, string salt)
        where T : SymmetricAlgorithm, new()
   {
      DeriveBytes rgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));

      SymmetricAlgorithm algorithm = new T();

      byte[] rgbKey = rgb.GetBytes(algorithm.KeySize >> 3);
      byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize >> 3);

      ICryptoTransform transform = algorithm.CreateEncryptor(rgbKey, rgbIV);

      using (MemoryStream buffer = new MemoryStream())
      {
         using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
         {
            using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
            {
               writer.Write(value);
            }
         }

         return Convert.ToBase64String(buffer.ToArray());
      }
   }

   public static string Decrypt<T>(string text, string password, string salt)
      where T : SymmetricAlgorithm, new()
   {
      DeriveBytes rgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));

      SymmetricAlgorithm algorithm = new T();

      byte[] rgbKey = rgb.GetBytes(algorithm.KeySize >> 3);
      byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize >> 3);

      ICryptoTransform transform = algorithm.CreateDecryptor(rgbKey, rgbIV);

      using (MemoryStream buffer = new MemoryStream(Convert.FromBase64String(text)))
      {
         using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Read))
         {
            using (StreamReader reader = new StreamReader(stream, Encoding.Unicode))
            {
               return reader.ReadToEnd();
            }
         }
      }
   }
}

This should satisfy any need to leverage one of the .NET class library's symmetric encryption ciphers. A few possible tweaks would include automatically choosing the highest block size for a given algorithm and experimenting with different key generation techniques. Key selection is important in cryptography, as a general rule a symmetric encryption algorithm is only as good as its key regardless of how secure the underlying algorithm is proven to be.