Click here to Skip to main content
15,896,201 members
Articles / Programming Languages / C#

Attribute-flavored Domain Driven Design

Rate me:
Please Sign up or sign in to vote.
4.00/5 (2 votes)
6 Apr 2008CPOL8 min read 39.2K   231   25  
A centralized business domain with a common base
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Codebasement.DomainDrivenDesign.Basement.Utilities
{
    /// <summary>
    /// 
    /// </summary>
    public enum SymmetricCryptoAlgorithm
    {
        /// <summary>
        /// 
        /// </summary>
        DES,
        /// <summary>
        /// 
        /// </summary>
        Rijndael
    }

    /// <summary>
    /// CallBack delegate for progress notification
    /// </summary>
    public delegate void CryptoProgressCallBack(int min, int max, int value);

    /// <summary>
    /// This is the exception class that is thrown throughout the Decryption process
    /// </summary>
    public class CryptoHelpException : ApplicationException
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CryptoHelpException"/> class.
        /// </summary>
        /// <param name="msg">The MSG.</param>
        public CryptoHelpException(string msg) : base(msg)
        {
        }
    }

    /// <summary>
    /// This class provide static methods for (symmetric) encryption and decryption.
    /// </summary>
    public class Encryption
    {
        // the key

        /// <summary>
        /// The amount of bytes to read from the file
        /// </summary>
        private const int BUFFER_SIZE = 128*1024;

        /// <summary>
        /// Tag to make sure this file is readable/decryptable by this class
        /// </summary>
        private const ulong FC_TAG = 0xFC010203040506CF;

        private const string Password = "key";

        #region File Encryption

        /// <summary>
        /// Crypto Random number generator for use in EncryptFile
        /// </summary>
        private static readonly RandomNumberGenerator rand = new RNGCryptoServiceProvider();

        /// <summary>
        /// Checks to see if two byte array are equal
        /// </summary>
        /// <param name="b1">the first byte array</param>
        /// <param name="b2">the second byte array</param>
        /// <returns>true if b1.Length == b2.Length and each byte in b1 is
        /// equal to the corresponding byte in b2</returns>
        private static bool CheckByteArrays(byte[] b1, byte[] b2)
        {
            if (b1.Length == b2.Length)
            {
                for (int i = 0; i < b1.Length; ++i)
                {
                    if (b1[i] != b2[i])
                        return false;
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// Creates a Rijndael SymmetricAlgorithm for use in EncryptFile and DecryptFile
        /// </summary>
        /// <param name="password">the string to use as the password</param>
        /// <param name="salt">the salt to use with the password</param>
        /// <returns>A SymmetricAlgorithm for encrypting/decrypting with Rijndael</returns>
        private static SymmetricAlgorithm CreateRijndael(string password, byte[] salt)
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt); //, "SHA256", 1000);

            SymmetricAlgorithm sma = Rijndael.Create();
            sma.KeySize = 256;
            sma.Key = pdb.GetBytes(32);
            sma.Padding = PaddingMode.PKCS7;
            return sma;
        }

        /// <summary>
        /// Creates a DES SymmetricAlgorithm for use in EncryptFile and DecryptFile
        /// </summary>
        /// <param name="password">the string to use as the password</param>
        /// <param name="salt">the salt to use with the password</param>
        /// <returns>A SymmetricAlgorithm for encrypting/decrypting with Rijndael</returns>
        private static SymmetricAlgorithm CreateDES(string password, byte[] salt)
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt);

            SymmetricAlgorithm sma = DES.Create();
            sma.KeySize = 64;
            sma.Key = pdb.GetBytes(8);
            return sma;
        }

        /// <summary>
        /// Generates a specified amount of random bytes
        /// </summary>
        /// <param name="count">the number of bytes to return</param>
        /// <returns>a byte array of count size filled with random bytes</returns>
        private static byte[] GenerateRandomBytes(int count)
        {
            byte[] bytes = new byte[count];
            rand.GetBytes(bytes);
            return bytes;
        }

        /// <summary>
        /// This takes an input file and encrypts it into the output file
        /// </summary>
        /// <param name="inFile">the file to encrypt</param>
        /// <param name="outFile">the file to write the encrypted data to</param>
        /// <param name="password">the password to use as the key</param>
        /// <param name="callback">the method to call to notify of progress</param>
        /// <param name="algorithm">The algorithm.</param>
        public static void EncryptFile(string inFile,
                                       string outFile,
                                       string password,
                                       CryptoProgressCallBack callback,
                                       SymmetricCryptoAlgorithm algorithm)
        {
            using (FileStream fin = File.OpenRead(inFile),
                              fout = File.OpenWrite(outFile))
            {
                long lSize = fin.Length; // the size of the input file for storing
                int size = (int) lSize; // the size of the input file for progress
                byte[] bytes = new byte[BUFFER_SIZE]; // the buffer
                int value = 0; // the amount overall read from the input file for progress


                SymmetricAlgorithm sma;
                byte[] IV;
                byte[] salt;

                // create the crypting object
                if (algorithm == SymmetricCryptoAlgorithm.Rijndael)
                {
                    // generate IV and Salt
                    IV = GenerateRandomBytes(16);
                    salt = GenerateRandomBytes(16);
                    sma = CreateRijndael(password, salt);
                }
                else
                {
                    // generate IV and Salt
                    IV = GenerateRandomBytes(8);
                    salt = GenerateRandomBytes(8);
                    sma = CreateDES(password, salt);
                }

                sma.IV = IV;

                // write the IV and salt to the beginning of the file
                fout.Write(IV, 0, IV.Length);
                fout.Write(salt, 0, salt.Length);

                // create the hashing and crypto streams
                HashAlgorithm hasher = SHA256.Create();

                using (CryptoStream cout = new CryptoStream(fout, sma.CreateEncryptor(), CryptoStreamMode.Write),
                                    chash = new CryptoStream(Stream.Null, hasher, CryptoStreamMode.Write))
                {
                    // write the size of the file to the output file
                    BinaryWriter bw = new BinaryWriter(cout);
                    bw.Write(lSize);

                    // write the file cryptor tag to the file
                    bw.Write(FC_TAG);

                    // read and the write the bytes to the crypto stream in BUFFER_SIZEd chunks
                    int read; // the amount of bytes read from the input file
                    while ((read = fin.Read(bytes, 0, bytes.Length)) != 0)
                    {
                        cout.Write(bytes, 0, read);
                        chash.Write(bytes, 0, read);
                        value += read;
                        callback(0, size, value);
                    }

                    // flush and close the hashing object
                    chash.Flush();
                    chash.Close();

                    // read the hash
                    byte[] hash = hasher.Hash;

                    // write the hash to the end of the file
                    cout.Write(hash, 0, hash.Length);

                    // flush and close the cryptostream
                    cout.Flush();
                    cout.Close();
                }
            }
        }


        /// <summary>
        /// takes an input file and decrypts it to the output file
        /// </summary>
        /// <param name="inFile">the file to decrypt</param>
        /// <param name="outFile">the file to write the decrypted data to</param>
        /// <param name="password">the password used as the key</param>
        /// <param name="callback">the method to call to notify of progress</param>
        /// <param name="algorithm">The algorithm.</param>
        public static void DecryptFile(string inFile,
                                       string outFile,
                                       string password,
                                       CryptoProgressCallBack callback,
                                       SymmetricCryptoAlgorithm algorithm)

        {
            // create and open the file streams
            using (FileStream fin = File.OpenRead(inFile),
                              fout = File.OpenWrite(outFile))
            {
                int size = (int) fin.Length; // the size of the file for progress notification
                byte[] bytes = new byte[BUFFER_SIZE]; // byte buffer
                int outValue = 0; // the amount of bytes written out

                byte[] IV;
                byte[] salt;
                SymmetricAlgorithm sma;

                // create the crypting object
                if (algorithm == SymmetricCryptoAlgorithm.Rijndael)
                {
                    IV = new byte[16];
                    fin.Read(IV, 0, 16);
                    salt = new byte[16];
                    fin.Read(salt, 0, 16);

                    sma = CreateRijndael(password, salt);
                }
                else
                {
                    IV = new byte[8];
                    fin.Read(IV, 0, 8);
                    salt = new byte[8];
                    fin.Read(salt, 0, 8);

                    sma = CreateDES(password, salt);
                }

                sma.IV = IV;

                int value = 32;
                long lSize; // the size stored in the input stream

                // create the hashing object, so that we can verify the file
                HashAlgorithm hasher = SHA256.Create();

                // create the cryptostreams that will process the file
                using (CryptoStream cin = new CryptoStream(fin, sma.CreateDecryptor(), CryptoStreamMode.Read),
                                    chash = new CryptoStream(Stream.Null, hasher, CryptoStreamMode.Write))
                {
                    // read size from file
                    BinaryReader br = new BinaryReader(cin);
                    lSize = br.ReadInt64();
                    ulong tag = br.ReadUInt64();

                    if (FC_TAG != tag)
                        throw new CryptoHelpException("File Corrupted!");

                    //determine number of reads to process on the file
                    long numReads = lSize/BUFFER_SIZE;

                    // determine what is left of the file, after numReads
                    long slack = lSize%BUFFER_SIZE;

                    // read the buffer_sized chunks
                    int read; // the amount of bytes read from the stream
                    for (int i = 0; i < numReads; ++i)
                    {
                        read = cin.Read(bytes, 0, bytes.Length);
                        fout.Write(bytes, 0, read);
                        chash.Write(bytes, 0, read);
                        value += read;
                        outValue += read;
                        callback(0, size, value);
                    }

                    // now read the slack
                    if (slack > 0)
                    {
                        read = cin.Read(bytes, 0, (int) slack);
                        fout.Write(bytes, 0, read);
                        chash.Write(bytes, 0, read);
                        value += read;
                        outValue += read;
                        callback(0, size, value);
                    }

                    // flush and close the hashing stream
                    chash.Flush();
                    chash.Close();

                    // flush and close the output file
                    fout.Flush();
                    fout.Close();

                    // read the current hash value
                    byte[] curHash = hasher.Hash;

                    // get and compare the current and old hash values
                    byte[] oldHash = new byte[hasher.HashSize/8];
                    read = cin.Read(oldHash, 0, oldHash.Length);
                    if ((oldHash.Length != read) || (!CheckByteArrays(oldHash, curHash)))
                        throw new CryptoHelpException("File Corrupted!");
                }

                // make sure the written and stored size are equal
                if (outValue != lSize)
                    throw new CryptoHelpException("File Sizes don't match!");
            }
        }

        #endregion File Encryption

        #region Password Hash

        /// <summary>
        /// Encrypts the password.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <param name="password">The password.</param>
        /// <param name="salted">if set to <c>true</c> [salted].</param>
        /// <param name="salt">The salt.</param>
        /// <returns></returns>
        public byte[] EncryptPassword(string userName, string password,
                                      bool salted, byte[] salt)
        {
            string tmpPassword;

            if (salted) // password + salt
            {
                tmpPassword = Convert.ToBase64String(salt)
                              + userName.ToLower() + password;
            }
            else
            {
                tmpPassword = password;
            }

            //Convert the password string into an Array of bytes.
            UTF8Encoding textConverter = new UTF8Encoding();
            byte[] passBytes = textConverter.GetBytes(tmpPassword);

            //Return the encrypted bytes
            return new SHA256Managed().ComputeHash(passBytes);
        }

        // Comparison of two arrays for equality
        // You can add a length comparison, but normally
        // all hashes are the same size.
/*
        private bool PasswordsMatch(byte[] psswd1, byte[] psswd2)
        {
            try
            {
                for (int i = 0; i < psswd1.Length; i++)
                {
                    if (psswd1[i] != psswd2[i])
                        return false;
                }
                return true;
            }
            catch (IndexOutOfRangeException)
            {
                return false;
            }
        }
*/

        #endregion Password Hash

        #region Symmetric String Encryption

        /// <summary>
        /// Method which does the encryption using Rijndael algorithm
        /// </summary>
        /// <param name="InputText">Data to be encrypted</param>
        /// <returns>Encrypted Data</returns>
        public static string EncryptRijndaelString(string InputText)
        {
            RijndaelManaged RijndaelCipher = new RijndaelManaged();

            byte[] PlainText = Encoding.Unicode.GetBytes(InputText);
            byte[] Salt = Encoding.ASCII.GetBytes(Password.Length.ToString());

            //This class uses an extension of the PBKDF1 algorithm defined in the PKCS#5 v2.0 
            //standard to derive bytes suitable for use as key material from a password. 
            //The standard is documented in IETF RRC 2898.

            //PasswordDeriveBytes SecretKey = new PasswordDeriveBytes(Password, Salt);
            Rfc2898DeriveBytes SecretKey = new Rfc2898DeriveBytes(Password, Salt); //, "SHA256", 1000);

            //Creates a symmetric encryptor object. 
            ICryptoTransform Encryptor = RijndaelCipher.CreateEncryptor(SecretKey.GetBytes(32), SecretKey.GetBytes(16));
            MemoryStream memoryStream = new MemoryStream();

            //Defines a stream that links data streams to cryptographic transformations
            CryptoStream cryptoStream = new CryptoStream(memoryStream, Encryptor, CryptoStreamMode.Write);
            cryptoStream.Write(PlainText, 0, PlainText.Length);

            //Writes the final state and clears the buffer
            cryptoStream.FlushFinalBlock();
            byte[] CipherBytes = memoryStream.ToArray();

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

            string EncryptedData = Convert.ToBase64String(CipherBytes);
            return EncryptedData;
        }


        /// <summary>
        /// Method which does the encryption using Rijndael algorithm.
        /// This is for decrypting the data
        /// which has orginally being encrypted using the above method
        /// </summary>
        /// <returns>Decrypted Data</returns>
        public static string DecryptRijndaelString(string InputText)
        {
            RijndaelManaged RijndaelCipher = new RijndaelManaged();

            byte[] EncryptedData = Convert.FromBase64String(InputText);

            byte[] Salt = Encoding.ASCII.GetBytes(Password.Length.ToString());

            //Making of the key for decryption
            Rfc2898DeriveBytes SecretKey = new Rfc2898DeriveBytes(Password, Salt); //, "SHA256", 1000);

            //Creates a symmetric Rijndael decryptor object.
            ICryptoTransform Decryptor = RijndaelCipher.CreateDecryptor(SecretKey.GetBytes(32), SecretKey.GetBytes(16));
            MemoryStream memoryStream = new MemoryStream(EncryptedData);

            //Defines the cryptographics stream for decryption. The stream contains decrypted data
            CryptoStream cryptoStream = new CryptoStream(memoryStream, Decryptor, CryptoStreamMode.Read);
            byte[] PlainText = new byte[EncryptedData.Length];
            int DecryptedCount = cryptoStream.Read(PlainText, 0, PlainText.Length);

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

            //Converting to string
            string DecryptedData = Encoding.Unicode.GetString(PlainText, 0, DecryptedCount);
            return DecryptedData;
        }


        /// <summary>
        /// Method which does the encryption using DES algorithm
        /// </summary>
        /// <param name="InputText">Data to be encrypted</param>
        /// <returns>Encrypted Data</returns>
        public static string EncryptDESString(string InputText)
        {
            DESCryptoServiceProvider DESCipher = new DESCryptoServiceProvider();

            byte[] PlainText = Encoding.Unicode.GetBytes(InputText);
            byte[] Salt = Encoding.ASCII.GetBytes(Password.Length.ToString());

            //This class uses an extension of the PBKDF1 algorithm defined in the PKCS#5 v2.0 
            //standard to derive bytes suitable for use as key material from a password. 
            //The standard is documented in IETF RRC 2898.

            Rfc2898DeriveBytes SecretKey = new Rfc2898DeriveBytes(Password, Salt); //, "SHA256", 1000);

            //Creates a symmetric encryptor object. 
            ICryptoTransform Encryptor = DESCipher.CreateEncryptor(SecretKey.GetBytes(8), SecretKey.GetBytes(8));
            MemoryStream memoryStream = new MemoryStream();

            //Defines a stream that links data streams to cryptographic transformations
            CryptoStream cryptoStream = new CryptoStream(memoryStream, Encryptor, CryptoStreamMode.Write);
            cryptoStream.Write(PlainText, 0, PlainText.Length);

            //Writes the final state and clears the buffer
            cryptoStream.FlushFinalBlock();
            byte[] CipherBytes = memoryStream.ToArray();

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

            string EncryptedData = Convert.ToBase64String(CipherBytes);
            return EncryptedData;
        }


        /// <summary>
        /// Method which does the encryption using DES algorithm.
        /// This is for decrypting the data
        /// which has orginally being encrypted using the above method
        /// </summary>
        /// <returns>Decrypted Data</returns>
        public static string DecryptDESString(string InputText)
        {
            DESCryptoServiceProvider DESCipher = new DESCryptoServiceProvider();

            byte[] EncryptedData = Convert.FromBase64String(InputText);

            byte[] Salt = Encoding.ASCII.GetBytes(Password.Length.ToString());

            //Making of the key for decryption
            Rfc2898DeriveBytes SecretKey = new Rfc2898DeriveBytes(Password, Salt); //, "SHA256", 1000);

            //Creates a symmetric decryptor object.
            ICryptoTransform Decryptor = DESCipher.CreateDecryptor(SecretKey.GetBytes(8), SecretKey.GetBytes(8));
            MemoryStream memoryStream = new MemoryStream(EncryptedData);

            //Defines the cryptographics stream for decryption. The stream contains decrypted data
            CryptoStream cryptoStream = new CryptoStream(memoryStream, Decryptor, CryptoStreamMode.Read);
            byte[] PlainText = new byte[EncryptedData.Length];
            int DecryptedCount = cryptoStream.Read(PlainText, 0, PlainText.Length);

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

            //Converting to string
            string DecryptedData = Encoding.Unicode.GetString(PlainText, 0, DecryptedCount);
            return DecryptedData;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="InputString"></param>
        /// <param name="EncryptedString"></param>
        /// <returns></returns>
        public static bool CompareRijndaelStrings(string InputString, string EncryptedString)
        {
            return EncryptRijndaelString(InputString) == EncryptedString;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="InputString"></param>
        /// <param name="EncryptedString"></param>
        /// <returns></returns>
        public static bool CompareDESStrings(string InputString, string EncryptedString)
        {
            return EncryptDESString(InputString) == EncryptedString;
        }

        #endregion Symmetric String Encryption
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Netherlands Netherlands
Software engineer & architect.

Comments and Discussions