HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

C# AES + RSA Encryption Implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
encryptionimplementationrsaaes

Problem

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

Solution

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();
}


  • CryptoStream.Flush is a no-op



  • Disposing of the CryptoStream will call FlushFinalBlock.



  • ToArray can still be called on the MemoryStream after it has been closed by disposing of the CryptoStream.



  • ToArray "writes the stream contents to a byte array, regardless of the Position property."



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.

Code Snippets

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;
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();
}
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;
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

Context

StackExchange Code Review Q#84922, answer score: 10

Revisions (0)

No revisions yet.