Using RijndaelManaged to encrypt/decrypt

The framework’s Cryptography model has 4 Symmetric encryption algorithms. I would ignore the ones with short key lengths and known vulnerabilities.

s1

In this example, I’m using RijndaelManaged with 256 bits key and 256 bits block size. Longer key/IV lengths provide better security and they will be slow. In the world of encryption, the slower, the better –- more difficult to crack. The default for RijndaelManaged is 265bits key and 128 bits block size. The BlockSize property determines the IV (Initialization Vector) size. MSDN says — the classes that derive from the SymmetricAlgorithm class use a chaining mode called cipher block chaining (CBC), which requires a key and an initialization vector to perform cryptographic transformations on data. To decrypt data that was encrypted using one of the SymmetricAlgorithm classes, you must set the Key property and IV property to the same values that were used for encryption. Here’s a relative comparision chart:-

s2

Here I do not store any kind of key anywhere. I generate key and IV using Rfc2898DeriveBytes class at runtime. The logic/implementation to generate the key/IV is public but not the key itself, which is never saved. Using tools like Reflector, it’s easy to reverse engineer and get the source code back. Hence the encryption dll needs to use something like, in-built Dotfuscator to protect source code theft. The implementation logic needs to be safeguarded. In real live scenarios, the whole key generation implementation can live on another system – as an example DB+WCF service – and the application asking for the key will have very very minimum access on the key storing box.However there is something (not the key) which has to be persisted. In my example here, it’s a string data which is different from the pattern, I am trying to encrypt.

Let’s say I am trying to encrypt the pattern “Framework cryptography”. Assume I develop a string, from the current user’s profile, like UserFirstName+DOB+email. So my passphrase, would be “John120187a1@a1.com”. This way, the passphrase stays unique for each user. Now based on this passphrase, I generate Salt with my own custom logic, which in turn is used to generate key/IV, that eventually gets used in encrypt decrypt functions.The Rfc2898DeriveBytes algorithm will always generate the same output if the input is same as before. Rfc2898DeriveBytes is an implementation of PBKDF2 (a password hashing method) and is slow and best so far.Here are those 2 functions:-

public static byte[] MyEncrypt(string strToBeEncrypted)
{
    //Plain Text to be encrypted
    byte[] PlainText = System.Text.Encoding.Unicode.GetBytes(strToBeEncrypted);

    //In live, get the persistant passphrase from other isolated source
    //This example has hardcoded passphrase just for demo purpose
    StringBuilder sb = new StringBuilder();
    sb.Append("John120187a1@a1.com");

    //Generate the Salt, with any custom logic and
    //using the above string
    StringBuilder _sbSalt = new StringBuilder();
    for (int i = 0; i < 8; i++)
    {
        _sbSalt.Append("," + sb.Length.ToString());
    }
    byte[] Salt = Encoding.ASCII.GetBytes(_sbSalt.ToString());

    //Key generation:- default iterations is 1000 
    //and recomended is 10000
    Rfc2898DeriveBytes pwdGen = new Rfc2898DeriveBytes(sb.ToString(), Salt, 10000);

    //The default key size for RijndaelManaged is 256 bits, 
    //while the default blocksize is 128 bits.
    RijndaelManaged _RijndaelManaged = new RijndaelManaged();
    _RijndaelManaged.BlockSize = 256; //Increased it to 256 bits- max and more secure

    byte[] key = pwdGen.GetBytes(_RijndaelManaged.KeySize / 8);   //This will generate a 256 bits key
    byte[] iv = pwdGen.GetBytes(_RijndaelManaged.BlockSize / 8);  //This will generate a 256 bits IV

    //On a given instance of Rfc2898DeriveBytes class,
    //GetBytes() will always return unique byte array.
    _RijndaelManaged.Key = key;
    _RijndaelManaged.IV = iv;

    //Now encrypt
    byte[] cipherText2 = null;
    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, _RijndaelManaged.CreateEncryptor(), CryptoStreamMode.Write))
        {
            cs.Write(PlainText, 0, PlainText.Length);
        }
        cipherText2 = ms.ToArray();
    }
    return cipherText2;
}
public static string MyDecrypt(byte[] cipherText2)
{
    //In live, get the persistant passphrase from other isolated source
    //This example has hardcoded passphrase just for demo purpose, obtained by 
    //adding current user's firstname + DOB + email
    //You may generate this string with any logic you want.
    StringBuilder sb = new StringBuilder();
    sb.Append("John120187a1@a1.com");

    //Generate the Salt, with any custom logic and
    //using the above string
    StringBuilder _sbSalt = new StringBuilder();
    for (int i = 0; i < 8; i++)
    {
        _sbSalt.Append("," + sb.Length.ToString());
    }
    byte[] Salt = Encoding.ASCII.GetBytes(_sbSalt.ToString());

    //Key generation:- default iterations is 1000 and recomended is 10000
    Rfc2898DeriveBytes pwdGen = new Rfc2898DeriveBytes(sb.ToString(), Salt, 10000);

    //The default key size for RijndaelManaged is 256 bits,
    //while the default blocksize is 128 bits.
    RijndaelManaged _RijndaelManaged = new RijndaelManaged();
    _RijndaelManaged.BlockSize = 256; //Increase it to 256 bits- more secure

    byte[] key = pwdGen.GetBytes(_RijndaelManaged.KeySize / 8);   //This will generate a 256 bits key
    byte[] iv = pwdGen.GetBytes(_RijndaelManaged.BlockSize / 8);  //This will generate a 256 bits IV

    //On a given instance of Rfc2898DeriveBytes class,
    //GetBytes() will always return unique byte array.
    _RijndaelManaged.Key = key;
    _RijndaelManaged.IV = iv;

    //Now decrypt
    byte[] plainText2 = null;
    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, _RijndaelManaged.CreateDecryptor(), CryptoStreamMode.Write))
        {
            cs.Write(cipherText2, 0, cipherText2.Length);
        }
        plainText2 = ms.ToArray();
    }
    //Decrypted text
    return System.Text.Encoding.Unicode.GetString(plainText2);
}
//Example usage 
MyEncrypt(string stringToBeEncrypted);
MyDecrypt(Convert.FromBase64String(string EncryptedString));

Encrypt/Decrypt is recommended in situations where you would want the original data back, like say a small personal tool to save n number of sensitive personal information OR last 4 digits of your CreditCard number, SSN etc.However for Password, hashing works best and NOT encrypting. If interested, you may want to explore more, things like DPAPI, ProtectedData class, SecureString.Thanks for reading.

Advertisements
This entry was posted in General ASP.Net C#. Bookmark the permalink.

2 Responses to Using RijndaelManaged to encrypt/decrypt

  1. Mike says:

    Nice article. I am seeing in some cases a Cryptographic Exception Padding is invalid and cannot be removed when trying to decrypt data. Any reason why this would be happening?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s