Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I've put together an AES encryption implementation to solve a set of requirements. I believe that I've met the requirements, but I'd like to see if anyone is willing to review the implementation to ensure that my crypto approach is sound.

Requirements

  • Static symmetric encryption API composed of Encrypt(string) and Decrypt(string) methods.
  • Multi-server use. Cannot use server scoped encryption. Previous Triple DES implementation did this.
  • Passphrase used to derive encryption key is stored as plaintext on disc (see "Notes" section below).

Encryption libraries used:

  • AES - System.Security.Cryptography.RijndaelManaged
  • RSA - System.Security.Cryptography.RSACryptoServiceProvider

Usage prerequisites

  • Server using encryption library must have certificate installed in certificate store.
  • Server using encryption library must have plain text passphrase present on disc.

Encryption Flow

  • Application requests encryption from AES library.
  • AES retrieves passphrase from disc.
  • 256 bit AES encryption key is derived from passphrase using certificate.
  • AES encryption is performed using key that is derived from RSA encryption.

Notes

I've omitted certain business requirements that are driving the requirements here for brevity. I do recognize that the additional server certificate security layer seems unnecessary without those details. The basic idea is that even if you know the base passphrase, you still need to have the certificate installed in order to encrypt/decrypt.

Code (semi-redacted)

AES

public static class AES
{
    private static readonly byte[] _Salt = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };
    private const int KEY_SIZE = 256;
    private const int BLOCK_SIZE = 128;
    private const PaddingMode PADDING_MODE = PaddingMode.PKCS7;
    private static readonly byte[] _AESKey = GetEncryptionKey(KEY_SIZE);

    public static string Encrypt(string dataToEncrypt)
    {
        if (dataToEncrypt == null || dataToEncrypt.Length < 1)
        {
            throw new ArgumentNullException("dataToEncrypt");
        }

        var data = Encoding.UTF8.GetBytes(dataToEncrypt);
        var encrypted = Encrypt(data);

        return Convert.ToBase64String(encrypted);
    }

    public static string Decrypt(string dataToDecrypt)
    {
        if (dataToDecrypt == null || dataToDecrypt.Length < 1)
        {
            throw new ArgumentNullException("dataToDecrypt");
        }

        var data = Convert.FromBase64String(dataToDecrypt);
        var decrypted = Decrypt(data);

        return Encoding.UTF8.GetString(decrypted);
    }

    public static byte[] Encrypt(byte[] dataToEncrypt)
    {
        if (dataToEncrypt == null || dataToEncrypt.Length < 1)
        {
            throw new ArgumentNullException("dataToEncrypt");
        }

        return Encrypt(
            dataToEncrypt,
            _AESKey
            );
    }

    public static byte[] Decrypt(byte[] dataToDecrypt)
    {
        if (dataToDecrypt == null || dataToDecrypt.Length < 1)
        {
            throw new ArgumentNullException("dataToDecrypt");
        }

        return Decrypt(
            dataToDecrypt,
            _AESKey
            );
    }

    private static byte[] Encrypt(byte[] dataToEncrypt, byte[] key)
    {
        byte[] encryptedData;

        using (var rij = new RijndaelManaged())
        {
            rij.KeySize = KEY_SIZE;
            rij.BlockSize = BLOCK_SIZE;
            rij.Padding = PADDING_MODE;
            rij.Key = key;
            rij.GenerateIV();

            ICryptoTransform encryptor = rij.CreateEncryptor(rij.Key, rij.IV);

            using (var memoryStream = new MemoryStream())
            {
                using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                {
                    using (var binaryWriter = new BinaryWriter(cryptoStream))
                    {
                        binaryWriter.Write(rij.IV);
                        binaryWriter.Write(dataToEncrypt);
                        cryptoStream.Flush();
                        cryptoStream.FlushFinalBlock();

                        memoryStream.Position = 0;
                        encryptedData = memoryStream.ToArray();
                    }



                    memoryStream.Close();
                    cryptoStream.Close();
                }
            }
        }

        return encryptedData;
    }

    private static byte[] Decrypt(byte[] dataToDecrypt, byte[] key)
    {
        byte[] decrypted;

        using (var rij = new RijndaelManaged())
        {
            rij.KeySize = KEY_SIZE;
            rij.BlockSize = BLOCK_SIZE;
            rij.Padding = PADDING_MODE;
            rij.Key = key;
            var iv = new byte[16];
            Array.Copy(dataToDecrypt, 0, iv, 0, iv.Length);
            rij.IV = iv;

            ICryptoTransform decryptor = rij.CreateDecryptor(rij.Key, rij.IV);

            using (var memoryStream = new MemoryStream())
            {
                using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
                {
                    using (var binaryWriter = new BinaryWriter(cryptoStream))
                    {
                        binaryWriter.Write(dataToDecrypt, iv.Length, dataToDecrypt.Length - iv.Length);
                        cryptoStream.Flush();
                        cryptoStream.FlushFinalBlock();
                        memoryStream.Position = 0;
                    }

                    decrypted = memoryStream.ToArray();
                    memoryStream.Close();
                    cryptoStream.Close();
                }
            }
        }

        return decrypted;
    }

    private static byte[] GenerateIV()
    {
        byte[] iv;

        using (var aes = new AesCryptoServiceProvider())
        {
            aes.GenerateIV();
            iv = aes.IV;
        }

        return iv;
    }

    private static byte[] GetEncryptionKey(int keySize)
    {
        byte[] key;
        var passphrase = @"some passphrase retrieved from server";

        var encryptedPassphrase = RSA.Encrypt(passphrase);
        var encryptedPassphraseBytes = new byte[encryptedPassphrase.Length * sizeof(char)];
        System.Buffer.BlockCopy(encryptedPassphrase.ToCharArray(), 0, encryptedPassphraseBytes, 0, encryptedPassphraseBytes.Length);

        return encryptedPassphraseBytes.Take(keySize / 8).ToArray();
    }
}

RSA

static class RSA
{
    private static X509Certificate2 GetEncryptionCertificate()
    {
        X509Certificate2 encryptionCertificate = null;
        X509Store certificateStore = new X509Store(StoreLocation.LocalMachine);
        certificateStore.Open(OpenFlags.ReadOnly);

        foreach (var certificate in certificateStore.Certificates)
        {
            if (certificate.FriendlyName == "NameOfCertificate")
            {
                encryptionCertificate = certificate;
                break;
            }
        }

        certificateStore.Close();

        if (encryptionCertificate == null)
        {
            throw new Exceptions.EncryptionCertificateNotFoundException();
        }

        return encryptionCertificate;
    }

    public static string Encrypt(string dataToEncrypt)
    {
        if (dataToEncrypt == null || dataToEncrypt.Length < 1)
        {
            throw new ArgumentNullException("dataToEncrypt");
        }

        var data = Encoding.UTF8.GetBytes(dataToEncrypt);
        var encrypted = Encrypt(data);

        return Convert.ToBase64String(encrypted);
    }

    public static string Decrypt(string dataToDecrypt)
    {
        if (dataToDecrypt == null || dataToDecrypt.Length < 1)
        {
            throw new ArgumentNullException("dataToDecrypt");
        }

        var data = Convert.FromBase64String(dataToDecrypt);
        var decrypted = Decrypt(data);

        return Encoding.UTF8.GetString(decrypted);
    }

    public static byte[] Encrypt(byte[] dataToEncrypt)
    {
        if (dataToEncrypt == null || dataToEncrypt.Length < 1)
        {
            throw new ArgumentNullException("dataToEncrypt");
        }

        byte[] encrypted;

        using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)GetEncryptionCertificate().PublicKey.Key)
        {
            encrypted = rsa.Encrypt(dataToEncrypt, false);
        }

        return encrypted;
    }

    public static byte[] Decrypt(byte[] dataToDecrypt)
    {
        if (dataToDecrypt == null || dataToDecrypt.Length < 1)
        {
            throw new ArgumentNullException("dataToDecrypt");
        }

        byte[] decrypted;

        using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)GetEncryptionCertificate().PrivateKey)
        {
            decrypted = rsa.Decrypt(dataToDecrypt, false);
        }

        return decrypted;
    }
}
share|improve this question

1 Answer 1

As far as I can tell, this

byte[] encryptedData;

// ...

using (var memoryStream = new MemoryStream())
{
    using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
    {
        using (var binaryWriter = new BinaryWriter(cryptoStream))
        {
            binaryWriter.Write(rij.IV);
            binaryWriter.Write(dataToEncrypt);
            cryptoStream.Flush();
            cryptoStream.FlushFinalBlock();

            memoryStream.Position = 0;
            encryptedData = memoryStream.ToArray();
        }



        memoryStream.Close();
        cryptoStream.Close();
    }
}

return encryptedData;

Can be replaced with this

using (var memoryStream = new MemoryStream())
{
    using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
    {
        cryptoStream.Write(rij.IV, 0, rij.IV.Length);
        cryptoStream.Write(dataToEncrypt, 0, dataToEncrypt.Length);
    }

    return memoryStream.ToArray();
}

Similarly, this

byte[] decrypted;

// ...

using (var memoryStream = new MemoryStream())
{
    using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
    {
        using (var binaryWriter = new BinaryWriter(cryptoStream))
        {
            binaryWriter.Write(dataToDecrypt, iv.Length, dataToDecrypt.Length - iv.Length);
            cryptoStream.Flush();
            cryptoStream.FlushFinalBlock();
            memoryStream.Position = 0;
        }

        decrypted = memoryStream.ToArray();
        memoryStream.Close();
        cryptoStream.Close();
    }
}

return decrypted;

Can be written

using (var memoryStream = new MemoryStream())
{
    using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
    {
        cryptoStream.Write(dataToDecrypt, iv.Length, dataToDecrypt.Length - iv.Length);
    }

    return memoryStream.ToArray();
}

dataToEncrypt == null || dataToEncrypt.Length < 1

All code like this can be replaced with

string.IsNullOrEmpty(dataToEncrypt)

byte[] iv;

using (var aes = new AesCryptoServiceProvider())
{
    aes.GenerateIV();
    iv = aes.IV;
}

return iv;

Can be written

using (var aes = new AesCryptoServiceProvider())
{
    aes.GenerateIV();
    return aes.IV;
}

Similar rewrites are possible in RSA.Encrypt(byte[]) and RSA.Decrypt(byte[]).


There's an unused variable key in AES.GetEncryptionKey.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.