The Cryptography PostSharp Aspects

Today I would like to write some aspects for security and encryption of sensitive data in our application. Because I am huge fun of the PostSharp Framework I will try to prepare two aspects. That will be EncryptData and DecryptData aspects. Also I need to be sure that my aspects work very fast. So I will try to measure working aspects with the ANTS Performance Profiler 6 that I have.

So let me start with simple requirement:

The EncryptAspect and the DecryptAspect should use symmetric algorithms to encrypt and decrypt array of byte.

OK, so I will try prepare resolution for that requirement in this blog entry. In .NET Framework we have four symmetric algorithms implemented. There are: RijndelManaged (formally known as AES), DES, RC2 or TripleDES. I do not know which of that is the best for my resolution and I will try examine performance of them all using the ANTS Performance Profiler 6.

So now it is time for design. First we need some generic common class called CommonEncryptEngine that can use any of our four cryptography implementation. Design will be simple, we use generic common class with TEncryptEngine type parameter and two methods. That will be an Encrypt and a Decrypt methods that will be used for encryption and decryption array of byte. When we will prepare common engine class we will be preparing EncryptAttribute and DecryptAttribute base on PostSharp OnMethodBoundaryAspect with one parameter for the constructor. That parameter will be a type for common class generic parameter TEncryptEngine. We will use reflection and in static constructor we prepare common crypt engine class.

OK, we have requirement and basic design. We can start some coding.

First will be the CommonEncryptEngine – common generic class.

namespace CryptoEngineAspects
{
  using System.Security.Cryptography;

  public class CommonEncryptEngine<TEncryptEngine>
    where TEncryptEngine : SymmetricAlgorithm, new() {

    private readonly TEncryptEngine cryptEngine;
    private readonly ICryptoTransform encryptor;
    private readonly ICryptoTransform decryptor;

    public CommonEncryptEngine(byte[] iv = null, byte[] key = null) {
      cryptEngine = new TEncryptEngine();

      if (iv != null && key != null) {
        cryptEngine.IV = iv;
        cryptEngine.Key = key;
      }
      else {
        cryptEngine.GenerateIV();
        cryptEngine.GenerateKey();
      }

      encryptor = cryptEngine.CreateEncryptor();
      decryptor = cryptEngine.CreateDecryptor();
    }

    public byte[] Encrypt(byte[] data) {
      return encryptor.TransformFinalBlock(data, 0, data.Length);
    }

    public byte[] Decrypt(byte[] data) {
      return decryptor.TransformFinalBlock(data, 0, data.Length);
    }
  }
}

OK, so this is base common class implementation. Now we want to check if its work correctly so we need some test class. For example, the CommonEncryptEngineTests generic class will be my implementation of checking and performance tests. So we can start with two basic tests and implementation that is shown below.

using System.Diagnostics;

namespace CryptoEngineAspects
{
  using System.IO;
  using System.Security.Cryptography;
  using System.Text;

  public class CommonEncryptEngineTests<TEncryptEngine>
    where TEncryptEngine : SymmetricAlgorithm, new() {

    private readonly CommonEncryptEngine<TEncryptEngine> engine
      = new CommonEncryptEngine<TEncryptEngine>();

    private void BaseTestTransformData(string stringData) {
      var data = ASCIIEncoding.ASCII.GetBytes(stringData);
      var encryptedData = engine.Encrypt(data);
      var decryptedData = engine.Decrypt(encryptedData);
      var retrunedData = ASCIIEncoding.ASCII.GetString(decryptedData);
      if (!stringData.Equals(retrunedData))
        throw new InvalidDataException(
          string.Format(
            "Transformation with encrypt and decrypt failed for type: {0}.",
            typeof (TEncryptEngine)));
    }

    private void Test1() {
      BaseTestTransformData("Hello Common Encryption");
    }

    private void Test2() {
      BaseTestTransformData("1 Bigger block of text.n" +
                            "2 Bigger block of text.n" +
                            "3 Bigger block of text.n" +
                            "4 Bigger block of text.n" +
                            "5 Bigger block of text.n");
    }

    public void RunAllChecking() {
      Test1();
      Test2();
    }

    public long RunAllPerformance(int count) {
      var stopwatch = Stopwatch.StartNew();
      for (int i = 0; i < count; i++)
      {
        Test1();
        Test2();
      }
      stopwatch.Stop();
      return stopwatch.ElapsedMilliseconds;
    }
  }
}

So, we have implementation and tests. We can now try how it works in some simple console application.

namespace CryptoEngineAspects
{
  using System.Security.Cryptography;

  class Program
  {
    static void Main() {
      new CommonEncryptEngineTests<RijndaelManaged>()
        .RunAllChecking();
      new CommonEncryptEngineTests<DESCryptoServiceProvider>()
        .RunAllChecking();
      new CommonEncryptEngineTests<RC2CryptoServiceProvider>()
        .RunAllChecking();
      new CommonEncryptEngineTests<TripleDESCryptoServiceProvider>()
        .RunAllChecking();
    }
  }
}

Ok that everything works correctly, because we haven’t any exception, so we can now measure performance very easy, we can run a TestAllPerformance method from test class and we can write into console time for transform strings for example 100 000 times. So we can try change a bit our console application and see the results.

namespace CryptoEngineAspects
{
  using System;
  using System.Security.Cryptography;

  class Program
  {
    static void Main() {
      Console.WriteLine("Runs of 200 000 (2 per iteration) transformations.");
      Console.WriteLine("RijndaelManaged transforms take about {0} ms.",
        new CommonEncryptEngineTests<RijndaelManaged>()
          .RunAllPerformance(100000));
      Console.WriteLine("DES transforms take about {0} ms.",
        new CommonEncryptEngineTests<DESCryptoServiceProvider>()
          .RunAllPerformance(100000));
      Console.WriteLine("RC2 transforms take about {0} ms.",
        new CommonEncryptEngineTests<RC2CryptoServiceProvider>()
          .RunAllPerformance(100000));
      Console.WriteLine("TripleDES transforms take about {0} ms.",
        new CommonEncryptEngineTests<TripleDESCryptoServiceProvider>()
          .RunAllPerformance(100000));
      Console.ReadKey();
    }
  }
}

An output of performance tests is shown below.

Runs of 200 000 (2 per iteration) transformations.
RijndaelManaged transforms take about 4818 ms.
DES transforms take about 3439 ms.
RC2 transforms take about 4240 ms.
TripleDES transforms take about 6397 ms.

As you can see DES algorithm is the fastest one. And now because we have implemented solutions of cryptography we can write two aspects.

namespace CryptoEngineAspects
{
  using System;
  using System.Globalization;
  using System.Reflection;

  public class CryptographyAspectsBsse
  {
    private readonly object cryptographyEngine;
    private readonly MethodInfo encryptMethod;
    private readonly MethodInfo decryptMethod;

    public CryptographyAspectsBsse(Type cryptographyType) {
      Type cryptography = typeof(CommonEncryptEngine<>)
        .MakeGenericType(cryptographyType);
      cryptographyEngine = Activator
        .CreateInstance(cryptography,
        BindingFlags.Default,
        null,
        new object[]{null, null},
        CultureInfo.CurrentCulture);
      encryptMethod = cryptography.GetMethod("Encrypt");
      decryptMethod = cryptography.GetMethod("Decrypt");
    }

    public byte[] Encrypt(byte[] data) {
      return (byte[])encryptMethod
        .Invoke(cryptographyEngine, new [] { data });
    }

    public byte[] Decrypt(byte[] data) {
      return (byte[])decryptMethod
        .Invoke(cryptographyEngine, new[] { data });
    }
  }
}

Now we need host that store our engine and give us an Encrypt and a Decrypt methods to use with the same random IV and Key.

namespace CryptoEngineAspects
{
  using System;
  using System.Runtime.CompilerServices;

  public class CryptographyAspectsHost
  {
    private static object locker = new object();
    private static CryptographyAspectsBsse engine;

    [MethodImpl(MethodImplOptions.Synchronized)]
    public static CryptographyAspectsBsse GetInstance(Type cryptographyType) {
      lock (locker) {
        if (engine == null)
          engine = new CryptographyAspectsBsse(cryptographyType);

        return engine;
      }
    }
  }
}

And at last we can prepare aspects.

namespace CryptoEngineAspects
{
  using System;
  using System.Security.Cryptography;
  using PostSharp.Aspects;

  [Serializable]
  public class EncryptAttribute : OnMethodBoundaryAspect
  {
    [NonSerialized]
    private CryptographyAspectsBsse engine;

    public override void OnSuccess(MethodExecutionArgs args)
    {
      if (!(args.ReturnValue is byte[]))
        return;

      if(engine == null)
        engine = CryptographyAspectsHost
          .GetInstance(typeof(DESCryptoServiceProvider));

      var data = (byte[])args.ReturnValue;
      args.ReturnValue = engine.Encrypt(data);
    }
  }
}
namespace CryptoEngineAspects
{
  using System;
  using System.Security.Cryptography;
  using PostSharp.Aspects;

  [Serializable]
  public class DecryptAttribute : OnMethodBoundaryAspect
  {
    [NonSerialized]
    private CryptographyAspectsBsse engine;

    public override void OnSuccess(MethodExecutionArgs args)
    {
      if (!(args.Arguments[0] is byte[]))
        return;

      if (engine == null)
        engine = CryptographyAspectsHost
          .GetInstance(typeof(DESCryptoServiceProvider));

      var data = (byte[])args.Arguments[0];
      args.ReturnValue = engine.Decrypt(data);
    }
  }
}

OK, that is my solutions for Cryptography with the PostSharp Aspects. And last code I would like to show is small test.

namespace CryptoEngineAspects
{
  using System;
  using System.Text;

  class Program
  {
    [Encrypt]
    static byte[] EncryptTest(byte[] data) {
      return data;
    }

    [Decrypt]
    static byte[] DecryptTest(byte[] data) {
      return data;
    }

    static void Main() {
      Console.WriteLine(
        ASCIIEncoding.ASCII.GetString(
          DecryptTest(
            EncryptTest(
              ASCIIEncoding.ASCII.GetBytes("Hello Crypted World!")))));
      Console.ReadKey();
    }
  }
}

And now it is time for conclusions. It is very easy to secure sensitive data with symmetric encryption algorithms implemented in .NET Framework. Fastest one is DES. Of course, it is your decision of using encryption what algorithm is best for you. I think that most secure is TripleDES but my conclusion of it is that it is slowest one. So, probably AES implementation will be a good choice. This is a compromise with security and computer time. As you can see the ANTS PP 6 is very useful to check and/or find code issue and it can be a very helpful tool. And you can also easily encapsulate solution into the PostSharp aspects. I hope you enjoy this blog entry.

Best regards,

P ;).

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.