///////////////////////////////////////////////////////////////////////////////
// SAMPLE: Illustrates symmetric key encryption and decryption using Rijndael
//         algorithm. In addition to performing encryption, this sample 
//         explains how to add a salt value (randomly generated bytes) to 
//         plain text before the value is encrypted. This can help reduce the
//         risk of dictionary attacks.
//
// To run this sample, create a new Visual C# project using the Console 
// Application template and replace the contents of the Module1.vb file with
// the code below.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
// Copyright (C) 2003 Obviex(TM). All rights reserved.
//
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
 
/// <summary>
/// This class uses a symmetric key algorithm (Rijndael/AES) to encrypt and
/// decrypt data. As long as it is initialized with the same constructor
/// parameters, the class will use the same key. Before performing encryption,
/// the class can prepend random bytes to plain text and generate different
/// encrypted values from the same plain text, encryption key, initialization
/// vector, and other parameters. This class is thread-safe.
/// </summary>
/// <remarks>
/// Be careful when performing encryption and decryption. There is a bug
/// ("feature"?) in .NET Framework, which causes corruption of encryptor/
/// decryptor if a cryptographic exception occurs during encryption/
/// decryption operation. To correct the problem, re-initialize the class
/// instance when a cryptographic exception occurs.
/// </remarks>
public class RijndaelEnhanced
{
    #region Private members
    // If hashing algorithm is not specified, use SHA-1.
    private static string  DEFAULT_HASH_ALGORITHM  = "SHA1";
 
    // If key size is not specified, use the longest 256-bit key.
    private static int     DEFAULT_KEY_SIZE        = 256;
 
    // Do not allow salt to be longer than 255 bytes, because we have only
    // 1 byte to store its length. 
    private static int     MAX_ALLOWED_SALT_LEN    = 255;
 
    // Do not allow salt to be smaller than 4 bytes, because we use the first
    // 4 bytes of salt to store its length. 
    private static int     MIN_ALLOWED_SALT_LEN    = 4;
 
    // Random salt value will be between 4 and 8 bytes long.
    private static int     DEFAULT_MIN_SALT_LEN    = MIN_ALLOWED_SALT_LEN;
    private static int     DEFAULT_MAX_SALT_LEN    = 8;
 
    // Use these members to save min and max salt lengths.
    private int     minSaltLen              = -1;
    private int     maxSaltLen              = -1;
 
    // These members will be used to perform encryption and decryption.
    private ICryptoTransform encryptor      = null;
    private ICryptoTransform decryptor      = null;
    #endregion
 
    #region Constructors
    /// <summary>
    /// Use this constructor if you are planning to perform encryption/
    /// decryption with 256-bit key, derived using 1 password iteration,
    /// hashing without salt, no initialization vector, electronic codebook
    /// (ECB) mode, SHA-1 hashing algorithm, and 4-to-8 byte long salt.
    /// </summary>
    /// <param name="passPhrase">
    /// Passphrase from which a pseudo-random password will be derived.
    /// The derived password will be used to generate the encryption key.
    /// Passphrase can be any string. In this example we assume that the
    /// passphrase is an ASCII string. Passphrase value must be kept in
    /// secret.
    /// </param>
    /// <remarks>
    /// This constructor is not recommended because it does not use
    /// initialization vector and uses the ECB cipher mode, which is less
    /// secure than the CBC mode.
    /// </remarks>
    public RijndaelEnhanced(string  passPhrase) : 
        this(passPhrase, null)
    {
    }
 
    /// <summary>
    /// Use this constructor if you are planning to perform encryption/
    /// decryption with 256-bit key, derived using 1 password iteration,
    /// hashing without salt, cipher block chaining (CBC) mode, SHA-1
    /// hashing algorithm, and 4-to-8 byte long salt.
    /// </summary>
    /// <param name="passPhrase">
    /// Passphrase from which a pseudo-random password will be derived.
    /// The derived password will be used to generate the encryption key.
    /// Passphrase can be any string. In this example we assume that the
    /// passphrase is an ASCII string. Passphrase value must be kept in
    /// secret.
    /// </param>
    /// <param name="initVector">
    /// Initialization vector (IV). This value is required to encrypt the
    /// first block of plaintext data. For RijndaelManaged class IV must be
    /// exactly 16 ASCII characters long. IV value does not have to be kept
    /// in secret.
    /// </param>
    public RijndaelEnhanced(string  passPhrase,
                            string  initVector) :
        this(passPhrase, initVector, -1)
    {
    }
 
    /// <summary>
    /// Use this constructor if you are planning to perform encryption/
    /// decryption with 256-bit key, derived using 1 password iteration,
    /// hashing without salt, cipher block chaining (CBC) mode, SHA-1 
    /// hashing algorithm, and 0-to-8 byte long salt.
    /// </summary>
    /// <param name="passPhrase">
    /// Passphrase from which a pseudo-random password will be derived.
    /// The derived password will be used to generate the encryption key
    /// Passphrase can be any string. In this example we assume that the
    /// passphrase is an ASCII string. Passphrase value must be kept in
    /// secret.
    /// </param>
    /// <param name="initVector">
    /// Initialization vector (IV). This value is required to encrypt the
    /// first block of plaintext data. For RijndaelManaged class IV must be
    /// exactly 16 ASCII characters long. IV value does not have to be kept
    /// in secret.
    /// </param>
    /// <param name="minSaltLen">
    /// Min size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is less than 4, the default min value will be used (currently 4
    /// bytes).
    /// </param>
    public RijndaelEnhanced(string  passPhrase,
                            string  initVector,
                            int     minSaltLen) :
        this(passPhrase, initVector, minSaltLen, -1)
    {
    }
 
    /// <summary>
    /// Use this constructor if you are planning to perform encryption/
    /// decryption with 256-bit key, derived using 1 password iteration,
    /// hashing without salt, cipher block chaining (CBC) mode, SHA-1
    /// hashing algorithm. Use the minSaltLen and maxSaltLen parameters to
    /// specify the size of randomly generated salt.
    /// </summary>
    /// <param name="passPhrase">
    /// Passphrase from which a pseudo-random password will be derived.
    /// The derived password will be used to generate the encryption key.
    /// Passphrase can be any string. In this example we assume that the
    /// passphrase is an ASCII string. Passphrase value must be kept in
    /// secret.
    /// </param>
    /// <param name="initVector">
    /// Initialization vector (IV). This value is required to encrypt the
    /// first block of plaintext data. For RijndaelManaged class IV must be
    /// exactly 16 ASCII characters long. IV value does not have to be kept
    /// in secret.
    /// </param>
    /// <param name="minSaltLen">
    /// Min size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is less than 4, the default min value will be used (currently 4
    /// bytes).
    /// </param>
    /// <param name="maxSaltLen">
    /// Max size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is negative or greater than 255, the default max value will be
    /// used (currently 8 bytes). If max value is 0 (zero) or if it is smaller
    /// than the specified min value (which can be adjusted to default value),
    /// salt will not be used and plain text value will be encrypted as is.
    /// In this case, salt will not be processed during decryption either.
    /// </param>
    public RijndaelEnhanced(string  passPhrase,
                            string  initVector,
                            int     minSaltLen,
                            int     maxSaltLen) :
        this(passPhrase, initVector, minSaltLen, maxSaltLen, -1)
    {
    }
 
    /// <summary>
    /// Use this constructor if you are planning to perform encryption/
    /// decryption using the key derived from 1 password iteration,
    /// hashing without salt, cipher block chaining (CBC) mode, and
    /// SHA-1 hashing algorithm.
    /// </summary>
    /// <param name="passPhrase">
    /// Passphrase from which a pseudo-random password will be derived.
    /// The derived password will be used to generate the encryption key.
    /// Passphrase can be any string. In this example we assume that the
    /// passphrase is an ASCII string. Passphrase value must be kept in
    /// secret.
    /// </param>
    /// <param name="initVector">
    /// Initialization vector (IV). This value is required to encrypt the
    /// first block of plaintext data. For RijndaelManaged class IV must be
    /// exactly 16 ASCII characters long. IV value does not have to be kept
    /// in secret.
    /// </param>
    /// <param name="minSaltLen">
    /// Min size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is less than 4, the default min value will be used (currently 4
    /// bytes).
    /// </param>
    /// <param name="maxSaltLen">
    /// Max size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is negative or greater than 255, the default max value will be 
    /// used (currently 8 bytes). If max value is 0 (zero) or if it is smaller
    /// than the specified min value (which can be adjusted to default value),
    /// salt will not be used and plain text value will be encrypted as is.
    /// In this case, salt will not be processed during decryption either.
    /// </param>
    /// <param name="keySize">
    /// Size of symmetric key (in bits): 128, 192, or 256.
    /// </param>
    public RijndaelEnhanced(string  passPhrase,
                            string  initVector,
                            int     minSaltLen,
                            int     maxSaltLen,
                            int     keySize) :
        this(passPhrase, initVector, minSaltLen, maxSaltLen, keySize, null)
    {
    }
 
    /// <summary>
    /// Use this constructor if you are planning to perform encryption/
    /// decryption using the key derived from 1 password iteration, hashing 
    /// without salt, and cipher block chaining (CBC) mode.
    /// </summary>
    /// <param name="passPhrase">
    /// Passphrase from which a pseudo-random password will be derived.
    /// The derived password will be used to generate the encryption key.
    /// Passphrase can be any string. In this example we assume that the
    /// passphrase is an ASCII string. Passphrase value must be kept in
    /// secret.
    /// </param>
    /// <param name="initVector">
    /// Initialization vector (IV). This value is required to encrypt the
    /// first block of plaintext data. For RijndaelManaged class IV must be
    /// exactly 16 ASCII characters long. IV value does not have to be kept
    /// in secret.
    /// </param>
    /// <param name="minSaltLen">
    /// Min size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is less than 4, the default min value will be used (currently 4
    /// bytes).
    /// </param>
    /// <param name="maxSaltLen">
    /// Max size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is negative or greater than 255, the default max value will be
    /// used (currently 8 bytes). If max value is 0 (zero) or if it is smaller
    /// than the specified min value (which can be adjusted to default value),
    /// salt will not be used and plain text value will be encrypted as is.
    /// In this case, salt will not be processed during decryption either.
    /// </param>
    /// <param name="keySize">
    /// Size of symmetric key (in bits): 128, 192, or 256.
    /// </param>
    /// <param name="hashAlgorithm">
    /// Hashing algorithm: "MD5" or "SHA1". SHA1 is recommended.
    /// </param>
    public RijndaelEnhanced(string  passPhrase,
                            string  initVector,
                            int     minSaltLen,
                            int     maxSaltLen, 
                            int     keySize,
                            string  hashAlgorithm) : 
        this(passPhrase, initVector, minSaltLen, maxSaltLen, keySize, 
             hashAlgorithm, null)
    {
    }
 
    /// <summary>
    /// Use this constructor if you are planning to perform encryption/
    /// decryption using the key derived from 1 password iteration, and
    /// cipher block chaining (CBC) mode.
    /// </summary>
    /// <param name="passPhrase">
    /// Passphrase from which a pseudo-random password will be derived.
    /// The derived password will be used to generate the encryption key.
    /// Passphrase can be any string. In this example we assume that the
    /// passphrase is an ASCII string. Passphrase value must be kept in
    /// secret.
    /// </param>
    /// <param name="initVector">
    /// Initialization vector (IV). This value is required to encrypt the
    /// first block of plaintext data. For RijndaelManaged class IV must be
    /// exactly 16 ASCII characters long. IV value does not have to be kept
    /// in secret.
    /// </param>
    /// <param name="minSaltLen">
    /// Min size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is less than 4, the default min value will be used (currently 4
    /// bytes).
    /// </param>
    /// <param name="maxSaltLen">
    /// Max size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is negative or greater than 255, the default max value will be
    /// used (currently 8 bytes). If max value is 0 (zero) or if it is smaller
    /// than the specified min value (which can be adjusted to default value),
    /// salt will not be used and plain text value will be encrypted as is.
    /// In this case, salt will not be processed during decryption either.
    /// </param>
    /// <param name="keySize">
    /// Size of symmetric key (in bits): 128, 192, or 256.
    /// </param>
    /// <param name="hashAlgorithm">
    /// Hashing algorithm: "MD5" or "SHA1". SHA1 is recommended.
    /// </param>
    /// <param name="saltValue">
    /// Salt value used for password hashing during key generation. This is
    /// not the same as the salt we will use during encryption. This parameter
    /// can be any string.
    /// </param>
    public RijndaelEnhanced(string  passPhrase,
                            string  initVector,
                            int     minSaltLen,
                            int     maxSaltLen, 
                            int     keySize,
                            string  hashAlgorithm,
                            string  saltValue) : 
        this(passPhrase, initVector, minSaltLen, maxSaltLen, keySize, 
             hashAlgorithm, saltValue, 1)
    {
    }
 
    /// <summary>
    /// Use this constructor if you are planning to perform encryption/
    /// decryption with the key derived from the explicitly specified
    /// parameters.
    /// </summary>
    /// <param name="passPhrase">
    /// Passphrase from which a pseudo-random password will be derived.
    /// The derived password will be used to generate the encryption key
    /// Passphrase can be any string. In this example we assume that the
    /// passphrase is an ASCII string. Passphrase value must be kept in
    /// secret.
    /// </param>
    /// <param name="initVector">
    /// Initialization vector (IV). This value is required to encrypt the
    /// first block of plaintext data. For RijndaelManaged class IV must be
    /// exactly 16 ASCII characters long. IV value does not have to be kept
    /// in secret.
    /// </param>
    /// <param name="minSaltLen">
    /// Min size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is less than 4, the default min value will be used (currently 4
    /// bytes).
    /// </param>
    /// <param name="maxSaltLen">
    /// Max size (in bytes) of randomly generated salt which will be added at
    /// the beginning of plain text before encryption is performed. When this
    /// value is negative or greater than 255, the default max value will be
    /// used (currently 8 bytes). If max value is 0 (zero) or if it is smaller
    /// than the specified min value (which can be adjusted to default value),
    /// salt will not be used and plain text value will be encrypted as is.
    /// In this case, salt will not be processed during decryption either.
    /// </param>
    /// <param name="keySize">
    /// Size of symmetric key (in bits): 128, 192, or 256.
    /// </param>
    /// <param name="hashAlgorithm">
    /// Hashing algorithm: "MD5" or "SHA1". SHA1 is recommended.
    /// </param>
    /// <param name="saltValue">
    /// Salt value used for password hashing during key generation. This is
    /// not the same as the salt we will use during encryption. This parameter
    /// can be any string.
    /// </param>
    /// <param name="passwordIterations">
    /// Number of iterations used to hash password. More iterations are
    /// considered more secure but may take longer.
    /// </param>
    public RijndaelEnhanced(string  passPhrase,
                            string  initVector,
                            int     minSaltLen,
                            int     maxSaltLen, 
                            int     keySize,
                            string  hashAlgorithm,
                            string  saltValue,
                            int     passwordIterations)
    {
        // Save min salt length; set it to default if invalid value is passed.
        if (minSaltLen < MIN_ALLOWED_SALT_LEN)
            this.minSaltLen = DEFAULT_MIN_SALT_LEN;
        else
            this.minSaltLen = minSaltLen;
 
        // Save max salt length; set it to default if invalid value is passed.
        if (maxSaltLen < 0 || maxSaltLen > MAX_ALLOWED_SALT_LEN)
            this.maxSaltLen = DEFAULT_MAX_SALT_LEN;
        else
            this.maxSaltLen = maxSaltLen;
 
        // Set the size of cryptographic key.
        if (keySize <= 0)
            keySize = DEFAULT_KEY_SIZE;
 
        // Set the name of algorithm. Make sure it is in UPPER CASE and does
        // not use dashes, e.g. change "sha-1" to "SHA1".
        if (hashAlgorithm == null)
            hashAlgorithm = DEFAULT_HASH_ALGORITHM;
        else
            hashAlgorithm = hashAlgorithm.ToUpper().Replace("-", "");
 
        // Initialization vector converted to a byte array.
        byte[] initVectorBytes = null;
 
        // Salt used for password hashing (to generate the key, not during
        // encryption) converted to a byte array.
        byte[] saltValueBytes  = null;
 
        // Get bytes of initialization vector.
        if (initVector == null)
            initVectorBytes = new byte[0];
        else
            initVectorBytes = Encoding.ASCII.GetBytes(initVector);
 
        // Get bytes of salt (used in hashing).
        if (saltValue == null)
            saltValueBytes = new byte[0];
        else
            saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
 
        // Generate password, which will be used to derive the key.
        PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                   passPhrase,
                                                   saltValueBytes,
                                                   hashAlgorithm,
                                                   passwordIterations);
 
        // Convert key to a byte array adjusting the size from bits to bytes.
        byte[] keyBytes = password.GetBytes(keySize / 8);
 
        // Initialize Rijndael key object.
        RijndaelManaged symmetricKey = new RijndaelManaged();
 
        // If we do not have initialization vector, we cannot use the CBC mode.
        // The only alternative is the ECB mode (which is not as good).
        if (initVectorBytes.Length == 0)
            symmetricKey.Mode = CipherMode.ECB;
        else
            symmetricKey.Mode = CipherMode.CBC;
 
        // Create encryptor and decryptor, which we will use for cryptographic
        // operations.
        encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
        decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
    }
    #endregion
 
    #region Encryption routines
    /// <summary>
    /// Encrypts a string value generating a base64-encoded string.
    /// </summary>
    /// <param name="plainText">
    /// Plain text string to be encrypted.
    /// </param>
    /// <returns>
    /// Cipher text formatted as a base64-encoded string.
    /// </returns>
    public string Encrypt(string plainText)
    {
        return Encrypt(Encoding.UTF8.GetBytes(plainText));
    }
 
    /// <summary>
    /// Encrypts a byte array generating a base64-encoded string.
    /// </summary>
    /// <param name="plainTextBytes">
    /// Plain text bytes to be encrypted.
    /// </param>
    /// <returns>
    /// Cipher text formatted as a base64-encoded string.
    /// </returns>
    public string Encrypt(byte[] plainTextBytes)
    {
        return Convert.ToBase64String(EncryptToBytes(plainTextBytes));
    }
 
    /// <summary>
    /// Encrypts a string value generating a byte array of cipher text.
    /// </summary>
    /// <param name="plainText">
    /// Plain text string to be encrypted.
    /// </param>
    /// <returns>
    /// Cipher text formatted as a byte array.
    /// </returns>
    public byte[] EncryptToBytes(string plainText)
    {
        return EncryptToBytes(Encoding.UTF8.GetBytes(plainText));
    }
 
    /// <summary>
    /// Encrypts a byte array generating a byte array of cipher text.
    /// </summary>
    /// <param name="plainTextBytes">
    /// Plain text bytes to be encrypted.
    /// </param>
    /// <returns>
    /// Cipher text formatted as a byte array.
    /// </returns>
    public byte[] EncryptToBytes(byte[] plainTextBytes)
    {
        // Add salt at the beginning of the plain text bytes (if needed).
        byte[] plainTextBytesWithSalt = AddSalt(plainTextBytes);
 
        // Encryption will be performed using memory stream.
        MemoryStream memoryStream = new MemoryStream();
        
        // Let's make cryptographic operations thread-safe.
        lock (this)
        {
            // To perform encryption, we must use the Write mode.
            CryptoStream cryptoStream = new CryptoStream(
                                               memoryStream, 
                                               encryptor,
                                                CryptoStreamMode.Write);
 
            // Start encrypting data.
            cryptoStream.Write( plainTextBytesWithSalt, 
                                0, 
                               plainTextBytesWithSalt.Length);
             
            // Finish the encryption operation.
            cryptoStream.FlushFinalBlock();
 
            // Move encrypted data from memory into a byte array.
            byte[] cipherTextBytes = memoryStream.ToArray();
               
            // Close memory streams.
            memoryStream.Close();
            cryptoStream.Close();
 
            // Return encrypted data.
            return cipherTextBytes;
        }
    }
    #endregion
 
    #region Decryption routines
    /// <summary>
    /// Decrypts a base64-encoded cipher text value generating a string result.
    /// </summary>
    /// <param name="cipherText">
    /// Base64-encoded cipher text string to be decrypted.
    /// </param>
    /// <returns>
    /// Decrypted string value.
    /// </returns>
    public string Decrypt(string cipherText)
    {
        return Decrypt(Convert.FromBase64String(cipherText));
    }
 
    /// <summary>
    /// Decrypts a byte array containing cipher text value and generates a
    /// string result.
    /// </summary>
    /// <param name="cipherTextBytes">
    /// Byte array containing encrypted data.
    /// </param>
    /// <returns>
    /// Decrypted string value.
    /// </returns>
    public string Decrypt(byte[] cipherTextBytes)
    {
        return Encoding.UTF8.GetString(DecryptToBytes(cipherTextBytes));
    }
 
    /// <summary>
    /// Decrypts a base64-encoded cipher text value and generates a byte array
    /// of plain text data.
    /// </summary>
    /// <param name="cipherText">
    /// Base64-encoded cipher text string to be decrypted.
    /// </param>
    /// <returns>
    /// Byte array containing decrypted value.
    /// </returns>
    public byte[] DecryptToBytes(string cipherText)
    {
        return DecryptToBytes(Convert.FromBase64String(cipherText));
    }
 
    /// <summary>
    /// Decrypts a base64-encoded cipher text value and generates a byte array
    /// of plain text data.
    /// </summary>
    /// <param name="cipherTextBytes">
    /// Byte array containing encrypted data.
    /// </param>
    /// <returns>
    /// Byte array containing decrypted value.
    /// </returns>
    public byte[] DecryptToBytes(byte[] cipherTextBytes)
    {
        byte[] decryptedBytes      = null;
        byte[] plainTextBytes      = null;
        int    decryptedByteCount  = 0;
        int    saltLen             = 0;
 
        MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
 
        // Since we do not know how big decrypted value will be, use the same
        // size as cipher text. Cipher text is always longer than plain text
        // (in block cipher encryption), so we will just use the number of
        // decrypted data byte after we know how big it is.
        decryptedBytes = new byte[cipherTextBytes.Length];
 
        // Let's make cryptographic operations thread-safe.
        lock (this)
        {
            // To perform decryption, we must use the Read mode.
            CryptoStream  cryptoStream = new CryptoStream(
                                               memoryStream,
                                               decryptor,
                                               CryptoStreamMode.Read);
 
            // Decrypting data and get the count of plain text bytes.
            decryptedByteCount  = cryptoStream.Read(decryptedBytes,
                                                    0, 
                                                    decryptedBytes.Length);
            // Release memory.
            memoryStream.Close();
            cryptoStream.Close();
        }
 
        // If we are using salt, get its length from the first 4 bytes of plain
        // text data.
        if (maxSaltLen > 0 && maxSaltLen >= minSaltLen)
        {
            saltLen =   (decryptedBytes[0] & 0x03) |
                        (decryptedBytes[1] & 0x0c) |
                        (decryptedBytes[2] & 0x30) |
                        (decryptedBytes[3] & 0xc0);
        }
 
        // Allocate the byte array to hold the original plain text (without salt).
        plainTextBytes = new byte[decryptedByteCount - saltLen];
 
        // Copy original plain text discarding the salt value if needed.
        Array.Copy(decryptedBytes, saltLen, plainTextBytes, 
                    0, decryptedByteCount - saltLen);
 
        // Return original plain text value.
        return plainTextBytes;
    }
    #endregion
 
    #region Helper functions
    /// <summary>
    /// Adds an array of randomly generated bytes at the beginning of the
    /// array holding original plain text value.
    /// </summary>
    /// <param name="plainTextBytes">
    /// Byte array containing original plain text value.
    /// </param>
    /// <returns>
    /// Either original array of plain text bytes (if salt is not used) or a
    /// modified array containing a randomly generated salt added at the 
    /// beginning of the plain text bytes. 
    /// </returns>
    private byte[] AddSalt(byte[] plainTextBytes)
    {
        // The max salt value of 0 (zero) indicates that we should not use 
        // salt. Also do not use salt if the max salt value is smaller than
        // the min value.
        if (maxSaltLen == 0 || maxSaltLen < minSaltLen)
            return plainTextBytes;
 
        // Generate the salt.
        byte[] saltBytes = GenerateSalt();
 
        // Allocate array which will hold salt and plain text bytes.
        byte[] plainTextBytesWithSalt = new byte[plainTextBytes.Length +
                                                 saltBytes.Length];
        // First, copy salt bytes.
        Array.Copy(saltBytes, plainTextBytesWithSalt, saltBytes.Length);
 
        // Append plain text bytes to the salt value.
        Array.Copy( plainTextBytes, 0, 
                    plainTextBytesWithSalt, saltBytes.Length,
                    plainTextBytes.Length);
 
        return plainTextBytesWithSalt;
    }
 
    /// <summary>
    /// Generates an array holding cryptographically strong bytes.
    /// </summary>
    /// <returns>
    /// Array of randomly generated bytes.
    /// </returns>
    /// <remarks>
    /// Salt size will be defined at random or exactly as specified by the
    /// minSlatLen and maxSaltLen parameters passed to the object constructor.
    /// The first four bytes of the salt array will contain the salt length
    /// split into four two-bit pieces.
    /// </remarks>
    private byte[] GenerateSalt()
    {
        // We don't have the length, yet.
        int saltLen = 0;
 
        // If min and max salt values are the same, it should not be random.
        if (minSaltLen == maxSaltLen)
            saltLen = minSaltLen;
        // Use random number generator to calculate salt length.
        else
            saltLen = GenerateRandomNumber(minSaltLen, maxSaltLen);
 
        // Allocate byte array to hold our salt.
        byte[] salt = new byte[saltLen];
 
        // Populate salt with cryptographically strong bytes.
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
 
        rng.GetNonZeroBytes(salt);
 
        // Split salt length (always one byte) into four two-bit pieces and
        // store these pieces in the first four bytes of the salt array.
        salt[0] = (byte)((salt[0] & 0xfc) | (saltLen & 0x03));
        salt[1] = (byte)((salt[1] & 0xf3) | (saltLen & 0x0c));
        salt[2] = (byte)((salt[2] & 0xcf) | (saltLen & 0x30));
        salt[3] = (byte)((salt[3] & 0x3f) | (saltLen & 0xc0));
 
        return salt;
    }
 
    /// <summary>
    /// Generates random integer.
    /// </summary>
    /// <param name="minValue">
    /// Min value (inclusive).
    /// </param>
    /// <param name="maxValue">
    /// Max value (inclusive).
    /// </param>
    /// <returns>
    /// Random integer value between the min and max values (inclusive).
    /// </returns>
    /// <remarks>
    /// This methods overcomes the limitations of .NET Framework's Random
    /// class, which - when initialized multiple times within a very short
    /// period of time - can generate the same "random" number.
    /// </remarks>
    private int GenerateRandomNumber(int minValue, int maxValue)
    {
        // We will make up an integer seed from 4 bytes of this array.
        byte[] randomBytes = new byte[4];
 
        // Generate 4 random bytes.
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        rng.GetBytes(randomBytes);
 
        // Convert four random bytes into a positive integer value.
        int seed = ((randomBytes[0] & 0x7f) << 24) |
                    (randomBytes[1]         << 16) |
                    (randomBytes[2]         << 8 ) |
                    (randomBytes[3]);
 
        // Now, this looks more like real randomization.
        Random  random  = new Random(seed);
 
        // Calculate a random number.
        return random.Next(minValue, maxValue+1);
    }
    #endregion
}
 
/// <summary>
/// Illustrates the use of RijndaelEnhanced class to encrypt and decrypt data
/// using a random salt value.
/// </summary>
public class RijndaelEnhancedTest
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        string  plainText  = "Hello, World!";    // original plaintext
        string  cipherText = "";                 // encrypted text
        string  passPhrase = "Pas5pr@se";        // can be any string
        string  initVector = "@1B2c3D4e5F6g7H8"; // must be 16 bytes
 
        // Before encrypting data, we will append plain text to a random
        // salt value, which will be between 4 and 8 bytes long (implicitly
        // used defaults).
        RijndaelEnhanced rijndaelKey = 
            new RijndaelEnhanced(passPhrase, initVector);
 
        Console.WriteLine(String.Format("Plaintext   : {0}\n", plainText));
 
        // Encrypt the same plain text data 10 time (using the same key,
        // initialization vector, etc) and see the resulting cipher text;
        // encrypted values will be different.
        for (int i=0; i<10; i++)
        {
            cipherText =rijndaelKey.Encrypt(plainText);
            Console.WriteLine(
                String.Format("Encrypted #{0}: {1}", i, cipherText));
            plainText =rijndaelKey.Decrypt(cipherText);
        }
 
        // Make sure we got decryption working correctly.
        Console.WriteLine(String.Format("\nDecrypted   :{0}", plainText));
    }
}
//
// END OF FILE
///////////////////////////////////////////////////////////////////////////////