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.

Attachments:

CipherUtility.zip

Published Saturday 03 March 2012, 07:59 PM by Keith
Filed under: C# Cryptography Security

13 Comments

# re: Symmetric Encryption in C#

Friday 23 March 2012, 05:02 PM by Sergey

Thanks! Your code saved my time.

# re: Symmetric Encryption in C#

Tuesday 03 April 2012, 02:23 PM by Walter Stabosz

Good article. Just so you know, PasswordDeriveBytes.GetBytes is obsolete. You should use Rfc2898DeriveBytes.GetBytes.

# re: Symmetric Encryption in C#

Tuesday 03 April 2012, 06:47 PM by Keith

Thanks, after doing some research it looks like you're correct, Rfc2898DeriveBytes uses the PBKDF2 algorithm which is stronger than the PBKDF1 algorithm (which is what PasswordDeriveBytes uses).

# re: Symmetric Encryption in C#

Tuesday 17 July 2012, 06:45 PM by David

Thank you!!!

# re: Symmetric Encryption in C#

Wednesday 18 July 2012, 11:58 AM by passrby

Hi, thanks for the great article and code.

Might I suggest rounding it off by with guidance on the "password" and "salt" parameters - would be handy for those really just needing a quick way of confidently encrypting/decrypting a string without getting into the details of cryptography.

# re: Symmetric Encryption in C#

Monday 17 September 2012, 09:09 AM by Phil Soady

Too add for people looking for nice easy to use solution
A copied and tweaked version from something I saw on StackOverflow

     
       // Generate a simple yet strong salt key.  Only alphas in this example, but you can pimp the example easily.
       // Use the RNGCryptoServiceProvider to get random bytes. So should be reasonably random
       //
       // name="maxSize">How much NaCl is required ?
       // returns Random alpha string you can use as SALT
       public static string GenerateSimpleSalt(int maxSize = 64)
       {
           var alphaSet = new char[64]; // use 62 for strict alpha... that random generator for alphas only
           //nicer results with set length * int i = 256. But still produces excellent random results.
           //alphaset plus 2.  Reduce to 62 if alpha requried
           alphaSet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890#!".ToCharArray();
           var crypto = new RNGCryptoServiceProvider();
           var bytes = new byte[maxSize];
           crypto.GetBytes(bytes); //get a bucket of very random bytes
           var tempSB = new StringBuilder(maxSize);
           foreach (var b in bytes)
           {   // use b , a random from 0-255 as the index to our source array. Just mod on length set
               tempSB.Append(alphaSet[b % (alphaSet.Length)]);
           }
           return tempSB.ToString();
       }

# re: Symmetric Encryption in C#

Wednesday 03 October 2012, 05:29 PM by Nick V

I'd just like to say this is a neat class! Thanks for sharing.

# re: Symmetric Encryption in C#

Tuesday 16 October 2012, 08:15 PM by Kaiser

This just happens to do the trick. Thanks for sharing.

# re: Symmetric Encryption in C#

Wednesday 14 November 2012, 05:20 PM by Scott Wojan

Beautiful... thanks for this!!

# re: Symmetric Encryption in C#

Saturday 05 January 2013, 12:24 AM by Sandy

Very Useful article... keep posting... :-)

# re: Symmetric Encryption in C# Symmetric Encryption in C#

Thursday 27 June 2013, 09:22 AM by Olumide

Thanks for sharing this beautiful code. You are generous!

# re: Symmetric Encryption in C#

Saturday 20 July 2013, 10:32 PM by Binosh

Thanks for the code and knowledge.

# re: Symmetric Encryption in C#

Tuesday 04 February 2014, 04:51 PM by RHise

Thank you for the excellent post! This is exactly what I was looking for. I did however make one tweak like you mentioned could be done. Instead of making the alphaSet variable made up of a constant string, I generated two Guids as my alpaString (ex. (Guid.NewGuid().ToString("N").ToLower() + Guid.NewGuid().ToString("N").ToUpper()).ToCharArray();)
Now I know that there is a slightly higher chance of duplicated characters, but you are generating your salt from two unique, concatenated string.

Leave a Comment

(required)
   

(required)
   


   

(required)
   

(required)
 

Valid XHTML 1.0 Strict