26

I need to implement AES 256 encryption /decryption and I haven't been able to find an example that works correctly.

MSDN suggests that I should use the AES class.

The Rijndael class is the predecessor of the Aes algorithm. You should use the Aes algorithm instead of Rijndael. For more information, see the entry The Differences Between Rijndael and AES in the .NET Security blog.

Could anyone point me in the direction of a good example using the AES class for AES256?

To add a little more clarity:

I have a cipher file that contains the shared key and a string of encrypted text. I need to decrypt the text and then validate it.

All the examples I've seen expect at least 2 parameters to perform the encryption/decryption.

Should I be able to infer the Initialisation vector and the key from the text in the cipher file?

This is an example of the text held in my cipher file:

ÊÚḱÌrá ƒ@†²;Ä;öDWnªóª©©¨¦L

TeamWild
  • 2,460
  • 8
  • 43
  • 53
  • 6
    The documentation for both the [AesManaged](http://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged.aspx) and [AesCryptoServiceProvider](http://msdn.microsoft.com/en-us/library/system.security.cryptography.aescryptoserviceprovider.aspx) class provides elaborate examples. If the examples don't work in a way you expect, please post a short but complete program that demonstrates the problem. – dtb Sep 13 '11 at 11:13
  • @dtb Thanks. I hadn't spotted the example in AesManaged as this is only available for .Net 4 and I'm currently using 3.5 but the example looks straight forward. – TeamWild Sep 13 '11 at 14:03
  • To decode the data, you'll need to know what cipher streaming mode it was initially encrypted with (usually CBC or ECB). ECB has no initialization vector. If CBC was used, then the first few bytes of the encrypted data could be the IV. Please provide all the information you have and post a complete valid sample (kez and encrypted data). Since the encrypted data is binary, you'll have to post it Base 64 encoded or in a hexadecimal representation. – Codo Sep 14 '11 at 09:38
  • @TeamWild The AesManaged class is available for .NET Framework 3.5. – Brettski Dec 14 '13 at 06:27
  • @TeamWild I am tackling the exact from now, you dealt with then. I am struggling to generate a hash that matches. Are you willing to share an end to end example? – Jon H Mar 16 '19 at 16:57
  • @Jon H, I wrote this post a long time and several companies ago an so I don't still have the code. It was also written for a government department so I probably wouldn't be allowed to share it anyway. – TeamWild Mar 18 '19 at 21:46

4 Answers4

11

Maybe this example listed here can help you out. Statement from the author

about 24 lines of code to encrypt, 23 to decrypt

Due to the fact that the link in the original posting is dead - here the needed code parts (c&p without any change to the original source)

  /*
  Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.*/
   
  #region Usings
  using System;
  using System.IO;
  using System.Security.Cryptography;
  using System.Text;
  #endregion
   
  namespace Utilities.Encryption
  {
      /// <summary>
      /// Utility class that handles encryption
      /// </summary>
      public static class AESEncryption
      {
          #region Static Functions
   
          /// <summary>
          /// Encrypts a string
          /// </summary>
          /// <param name="PlainText">Text to be encrypted</param>
          /// <param name="Password">Password to encrypt with</param>
          /// <param name="Salt">Salt to encrypt with</param>
          /// <param name="HashAlgorithm">Can be either SHA1 or MD5</param>
          /// <param name="PasswordIterations">Number of iterations to do</param>
          /// <param name="InitialVector">Needs to be 16 ASCII characters long</param>
          /// <param name="KeySize">Can be 128, 192, or 256</param>
          /// <returns>An encrypted string</returns>
          public static string Encrypt(string PlainText, string Password,
              string Salt = "Kosher", string HashAlgorithm = "SHA1",
              int PasswordIterations = 2, string InitialVector = "OFRna73m*aze01xY",
              int KeySize = 256)
          {
              if (string.IsNullOrEmpty(PlainText))
                  return "";
              byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
              byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
              byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
              PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
              byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
              RijndaelManaged SymmetricKey = new RijndaelManaged();
              SymmetricKey.Mode = CipherMode.CBC;
              byte[] CipherTextBytes = null;
              using (ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes))
              {
                  using (MemoryStream MemStream = new MemoryStream())
                  {
                      using (CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write))
                      {
                          CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
                          CryptoStream.FlushFinalBlock();
                          CipherTextBytes = MemStream.ToArray();
                          MemStream.Close();
                          CryptoStream.Close();
                      }
                  }
              }
              SymmetricKey.Clear();
              return Convert.ToBase64String(CipherTextBytes);
          }
   
          /// <summary>
          /// Decrypts a string
          /// </summary>
          /// <param name="CipherText">Text to be decrypted</param>
          /// <param name="Password">Password to decrypt with</param>
          /// <param name="Salt">Salt to decrypt with</param>
          /// <param name="HashAlgorithm">Can be either SHA1 or MD5</param>
          /// <param name="PasswordIterations">Number of iterations to do</param>
          /// <param name="InitialVector">Needs to be 16 ASCII characters long</param>
          /// <param name="KeySize">Can be 128, 192, or 256</param>
          /// <returns>A decrypted string</returns>
          public static string Decrypt(string CipherText, string Password,
              string Salt = "Kosher", string HashAlgorithm = "SHA1",
              int PasswordIterations = 2, string InitialVector = "OFRna73m*aze01xY",
              int KeySize = 256)
          {
              if (string.IsNullOrEmpty(CipherText))
                  return "";
              byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
              byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
              byte[] CipherTextBytes = Convert.FromBase64String(CipherText);
              PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
              byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
              RijndaelManaged SymmetricKey = new RijndaelManaged();
              SymmetricKey.Mode = CipherMode.CBC;
              byte[] PlainTextBytes = new byte[CipherTextBytes.Length];
              int ByteCount = 0;
              using (ICryptoTransform Decryptor = SymmetricKey.CreateDecryptor(KeyBytes, InitialVectorBytes))
              {
                  using (MemoryStream MemStream = new MemoryStream(CipherTextBytes))
                  {
                      using (CryptoStream CryptoStream = new CryptoStream(MemStream, Decryptor, CryptoStreamMode.Read))
                      {
   
                          ByteCount = CryptoStream.Read(PlainTextBytes, 0, PlainTextBytes.Length);
                          MemStream.Close();
                          CryptoStream.Close();
                      }
                  }
              }
              SymmetricKey.Clear();
              return Encoding.UTF8.GetString(PlainTextBytes, 0, ByteCount);
          }
   
          #endregion
      }
  }
Dimi Takis
  • 4,924
  • 3
  • 29
  • 41
  • 1
    This bad (read insecure and redundant) code: salt should be random, password iterations are much too low, initialization vector should be random, the Close statements are unnecessary, FlushFinalBlock is unnecessary and `CipherTextBytes = MemStream.ToArray();` should be outside the CryptoStream using block. If you use the default settings of RijndaelManaged where it already generates a random key and IV you don't even need the PasswordDeriveBytes key derivation function. – ckuri Feb 03 '21 at 15:15
  • 1
    Thank you!!! I changed salts and such, but finally you are the first one I have come across in days of searching that provided a WORKING example of using AES algorithm. Also, since I am writing a back-end web application, I wrote wrapper functions that use predefined passwords, so just `Encrypt(string)` and `Decrypt(string)` to simplify it. I should add I modernized the code, instead of `RijndaelManaged` line, I used `var SymmetryKey = Aes.Create()` – Jay Imerman Dec 23 '22 at 14:45
  • @ckuri You are basically right :) - but (there's always a but) the original posting of mine was 12 years ago and the sample code too .... – Dimi Takis Feb 09 '23 at 06:05
2

Once I'd discovered all the information of how my client was handling the encryption/decryption at their end it was straight forward using the AesManaged example suggested by dtb.

The finally implemented code started like this:

    try
    {
        // Create a new instance of the AesManaged class.  This generates a new key and initialization vector (IV).
        AesManaged myAes = new AesManaged();

        // Override the cipher mode, key and IV
        myAes.Mode = CipherMode.ECB;
        myAes.IV = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // CRB mode uses an empty IV
        myAes.Key = CipherKey;  // Byte array representing the key
        myAes.Padding = PaddingMode.None;

        // Create a encryption object to perform the stream transform.
        ICryptoTransform encryptor = myAes.CreateEncryptor();

        // TODO: perform the encryption / decryption as required...

    }
    catch (Exception ex)
    {
        // TODO: Log the error 
        throw ex;
    }
TeamWild
  • 2,460
  • 8
  • 43
  • 53
  • 8
    I would not recommend using the ECB cipher mode. Use CBC or something else. If you need to know why, take a look at this Wikipedia Article: http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation. Also, here is a good example of how to do encryption with AES in C#: https://github.com/jbubriski/Encryptamajig – John B Jan 30 '12 at 14:59
  • 2
    Your IV should also be randomized, each time you're encrypting different data. You're also referencing CRB mode in the comment... – John B Jan 30 '12 at 15:38
  • 1
    @JohnBubriski - The third party that has supplied the encrypted file is insisted on using the cipher mode and IV. I understand that's not how you should do it but it was a case of "Fit in or F**k off". – TeamWild Mar 22 '12 at 14:49
  • 2
    Ouch, just make sure you cover your @$$! – John B Mar 26 '12 at 13:45
  • 1
    Isn't this example code using AES128? At least when I print out myAes.BlockSize I get 128 (although perhaps that would change if I used ECB). – Jason Axelson Feb 05 '13 at 00:52
  • 4
    AES has fixed BLOCK size of 128 bits, and a variable KEY size of 128, 192, or 256 bits. The key size is what discerns AES-128 from AES-256, block size is always 128. – mcdonams Oct 03 '16 at 18:16
  • 1
    i know this is an older thread, but it did show up in a google search today (November 2020) and after some research, it looks like the recommendation is to not use CBC mode any more. https://learn.microsoft.com/en-us/dotnet/standard/security/vulnerabilities-cbc-mode – isandburn Nov 04 '20 at 18:43
0
public class AesCryptoService
{
    private static byte[] Key = Encoding.ASCII.GetBytes(@"qwr{@^h`h&_`50/ja9!'dcmh3!uw<&=?");
    private static byte[] IV = Encoding.ASCII.GetBytes(@"9/\~V).A,lY&=t2b");


    public static string EncryptStringToBytes_Aes(string plainText)
    {
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV");
        byte[] encrypted;


        using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
        {
            aesAlg.Key = Key;
            aesAlg.IV = IV;
            aesAlg.Mode = CipherMode.CBC;
            aesAlg.Padding = PaddingMode.PKCS7;

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

            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        swEncrypt.Write(plainText);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        }
        
        return Convert.ToBase64String(encrypted);
    }


    
    public static string DecryptStringFromBytes_Aes(string Text)
    {
        if (Text == null || Text.Length <= 0)
            throw new ArgumentNullException("cipherText");
        if (Key == null || Key.Length <= 0)
            throw new ArgumentNullException("Key");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV");

        string plaintext = null;
        byte[] cipherText = Convert.FromBase64String(Text.Replace(' ', '+'));

        using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
        {
            aesAlg.Key = Key;
            aesAlg.IV = IV;
            aesAlg.Mode = CipherMode.CBC;
            aesAlg.Padding = PaddingMode.PKCS7;


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

            using (MemoryStream msDecrypt = new MemoryStream(cipherText))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }

        }

        return plaintext;
    }
}
  • 3
    This is insecure: the IV should be randomly generated everytime and not be static. The AesCryptoServiceProvider already generates a random IV, so there is no need (badly) precalculate one. A more proper appraoch would be to use than random IV from AesCryptoServiceProvider and write it to the MemoryStream. Decrypt would first read the IV from the MemoryStream and start decrypting. Also instead of a random ASCII key which severly limits your keyspace - instead of the intended AES-256 this here is virtually AES-32 - the key should be a cryptographically random bytes. – ckuri Feb 03 '21 at 15:26
0

Since both RijndaelManaged and AesManaged classes are deprecated, I am including the updated modern representation below. Also please take into account the discussions in this thread about IV generation and such, but this is plenty good enough for our application.

/*
  Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.*/

#region Usings
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
#endregion

namespace Utilities.Encryption
{
    public class AESEncryption
    {
        private const string _ENCRYPT_PASSWORD = "16BytePasswordz!";

        #region Static Functions

        public static string Encrypt(string PlainText)
        {
            return Encrypt(PlainText, _ENCRYPT_PASSWORD);
        }

        public static string Decrypt(string PlainText)
        {
            return Decrypt(PlainText, _ENCRYPT_PASSWORD);
        }

        /// <summary>
        /// Encrypts a string
        /// </summary>
        /// <param name="PlainText">Text to be encrypted</param>
        /// <param name="Password">Password to encrypt with</param>
        /// <param name="Salt">Salt to encrypt with</param>
        /// <param name="HashAlgorithm">Can be either SHA1 or MD5</param>
        /// <param name="PasswordIterations">Number of iterations to do</param>
        /// <param name="InitialVector">Needs to be 16 ASCII characters long</param>
        /// <param name="KeySize">Can be 128, 192, or 256</param>
        /// <returns>An encrypted string</returns>
        public static string Encrypt(string PlainText, string Password,
            string Salt = "Kosher", string HashAlgorithm = "SHA1",
            int PasswordIterations = 2, string InitialVector = "OFRna73m*aze01xY",
            int KeySize = 256)
        {
            if (string.IsNullOrEmpty(PlainText))
                return "";
            byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
            byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
            byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
            PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
            byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
            var SymmetricKey = Aes.Create();
            SymmetricKey.Mode = CipherMode.CBC;
            byte[]? CipherTextBytes = null;
            using (ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes))
            {
                using (MemoryStream MemStream = new MemoryStream())
                {
                    using (CryptoStream CryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write))
                    {
                        CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
                        CryptoStream.FlushFinalBlock();
                        CipherTextBytes = MemStream.ToArray();
                        MemStream.Close();
                        CryptoStream.Close();
                    }
                }
            }
            SymmetricKey.Clear();
            return Convert.ToBase64String(CipherTextBytes);
        }

        /// <summary>
        /// Decrypts a string
        /// </summary>
        /// <param name="CipherText">Text to be decrypted</param>
        /// <param name="Password">Password to decrypt with</param>
        /// <param name="Salt">Salt to decrypt with</param>
        /// <param name="HashAlgorithm">Can be either SHA1 or MD5</param>
        /// <param name="PasswordIterations">Number of iterations to do</param>
        /// <param name="InitialVector">Needs to be 16 ASCII characters long</param>
        /// <param name="KeySize">Can be 128, 192, or 256</param>
        /// <returns>A decrypted string</returns>
        public static string Decrypt(string CipherText, string Password,
            string Salt = "Kosher", string HashAlgorithm = "SHA1",
            int PasswordIterations = 2, string InitialVector = "OFRna73m*aze01xY",
            int KeySize = 256)
        {
            if (string.IsNullOrEmpty(CipherText))
                return "";
            byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
            byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
            byte[] CipherTextBytes = Convert.FromBase64String(CipherText);
            PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
            byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
            var SymmetricKey = Aes.Create();
            SymmetricKey.Mode = CipherMode.CBC;
            byte[] PlainTextBytes = new byte[CipherTextBytes.Length];
            int ByteCount = 0;
            using (ICryptoTransform Decryptor = SymmetricKey.CreateDecryptor(KeyBytes, InitialVectorBytes))
            {
                using (MemoryStream MemStream = new MemoryStream(CipherTextBytes))
                {
                    using (CryptoStream CryptoStream = new CryptoStream(MemStream, Decryptor, CryptoStreamMode.Read))
                    {

                        ByteCount = CryptoStream.Read(PlainTextBytes, 0, PlainTextBytes.Length);
                        MemStream.Close();
                        CryptoStream.Close();
                    }
                }
            }
            SymmetricKey.Clear();
            return Encoding.UTF8.GetString(PlainTextBytes, 0, ByteCount);
        }

        #endregion
    }
}


Jay Imerman
  • 4,475
  • 6
  • 40
  • 55