Wednesday 4 December 2013

Symmetric Encryption and Decryption

Symmetric encryption is the oldest cryptographic technique used to transmit or store digital data  securely.
Data that has been encrypted with symmetric encryption is considered to be confidential, in the sense that only those entities (persons or systems) who have the key can understand what the data means. 
Cryptography is a security-related technology, but it works differently to others. Rather than preventing unauthorized entities from accessing the information, cryptography prevents them from understanding what the data means. Notice that encrypted data is still data, but without meaning in relation to its purpose. So, the encryption process transforms data into other data that has no meaning.
With symmetric encryption, confidentiality is guaranteed by the use of a secret key. With this key, and a mathematical algorithm, data is converted to an unintelligible form. By using the same key, and applying the mathematical algorithm in the reverse way, the original data can be reconstituted. The algorithm is said to be a two-way algorithm.
Symmetric algorithms can be divided into two main classes: stream algorithms and block algorithms. Stream Algorithms operate directly on a stream of bytes whereas block algorithms  operate by transforming fixed-length groups of bits, a block,  at a time Block algorithms are most commonly used in the IT world today.
This article explains which symmetric algorithms are implemented in the .NET framework base classes, and how to apply them to encrypt and decrypt data.

Symmetric Encryption overview

Before I describe how to use symmetric encryption in the .NET framework, I should say a bit about how block symmetric algorithms work.
We have already mentioned that block symmetric algorithms work on blocks of data at a time. The data to be encrypted is divided into blocks of bits of fixed length and a transformation involving the secret key is applied on each block. The type of transformation depends on the selected algorithm. 
At this stage, it is important to notice that identical blocks of data are transformed in the same manner inside the blocks sequence. This can be a security issue. Think about an image file. This is composed of blocks of data that, depending on the image format, might set the color of a pixel on the screen. Suppose you want to encrypt this image.  Suppose, also, that you happen to set, as block unit for the image,  the same length of block that specifies a pixel color. By encrypting it, the “pixel-block” will be encrypted into a different block of data. Remember what we say in the introduction. Encrypted data is again data, but with a different meaning. The encryption transforms a block of bytes into a different block of bytes and so a color would, in those circumstances, become a different color.  If the identical blocks are encrypted in the same manner, we obtain an image with colors changed. The result is that the image is still intelligible to the user that sees it. 
To solve this issue, algorithms designers introduced different way to use the same symmetric algorithms on the data blocks. Those are referred as Chiper Modes. The most common approach is to take the previous encrypted data block and combine it with the current data block. This introduces some sort of randomness on the output encrypted blocks sequence. The first block is combined with a dummy initial block, named the initialization vector (IV). Sometimes data is not available block by block (as data that comes from a network stream) and the encryption system should retain data in memory until all blocks arrive. This could be dangerous from a security point of view. The issue can be solved by what is referred to as feedback. Small amount of data than those that forms a block are combined with an equal portion of the previous encrypted block until all the block arrives. The final combined block is  then encrypted.
Another issue comes from the block nature of the symmetric algorithm. If the initial data isn’t an exact multiple of the block size utilized, the final block cannot be encrypted. Again, algorithms designers get around this problem by padding the final block with extra data to the required  length. ‘Padding’ refers to the method used to expand the final data block.
The way in which Padding works is not the only influence on the security of the cryptography algorithm. Another factor is the length of the key used.  The longer the key, the more secure the encrypted data. The trade-off is that  more computational resources are required. For today’s needs, keys of 128-bit or 256-bit length are adopted.

Symmetric Encryption and .NET Framework

The .NET Framework rationalizes the block symmetric algorithms implemented on it by defining the base abstract class SymmetricAlgorithm that you can find under the System.Security.Cryptography namespace. Each symmetric algorithm is derived from it.
The main principal properties defined on SymmetricAlgorithm class are given by:


All the classes that perform symmetric encryption inherit or derive the previous properties. Those classes, contained on the same namespace of the SymmetricAlgorithm class, are given by:


SymmetricAlgorithm
 Aes
             AesCryptoServiceProvider
            ∟  AesManaged
 Rjindael
                            ∟  RjindaelManaged
∟  TripleDES
            ∟  TripleDESCryptoServiceProvider
∟  DES
            ∟  DESCryptoServiceProvider
∟  RC2
            ∟  RC2CryptoServiceProvider

As you can see, algorithms implemented by .NET Framework are: AES, Rjindael, TripleDES, DES and RC2.
Classes on the first level on the derivation tree (the classes Aes ,Rjindael, TripleDES, DES and RC2) define properties related to the specific algorithm. They also implement  the static Create() method that allows us  to create an instance of the object.
The second level on the derivation tree contains all the methods needed by the SymmetricAlgorithm derived object to operate. The two principal methods are given by:



They received, as input, the secret key and the initial IV; and they return a special object that implements the ICryptoTransform interface. We will call this object the Encryptor if we want to encrypt data, otherwise we call it the Decryptor.
This object has methods that permit it to transform arrays of bytes in their encrypted counterpart and vice versa. It is used in conjunction with the CryptoStream object. You can find it under the System.Security.Cryptographynamespace.
We will see shortly how to use this object to encrypt/decrypt data with the .NET framework.
Notice that some classes on the second derivation tree end with the suffix CryptoServiceProvider, others with the suffix Managed. The first class of objects utilizes native code to perform cryptographic operation (that relies on the Cryptographic Services Providers defined on the Microsoft® CryptoApi), the second try to utilize only Managed code.

Encrypt and Decrypt Data with .NET Framework

We are now able to use the .NET  Framework base classes to perform encryption and decryption of data.
We will encrypt and decrypt the string “Hello World … !” using, as symmetric algorithm, the Advance Encryption Standard (AES) algorithm.
To do so, we must: 
  1. Create an AesCryptoServiceProvider (or an AesManaged) object.
  2. Initialize it with a secret key and an IV (as bytes arrays)
  3. Create the Encryptor or Decryptor object.
  4. Use it in conjunction with a CryptoStream objects that will encrypt and decrypt our data.
We start by creating the secret key and the IV. We need to know their length first. To select the right key and IV lengths, we can browse the LegalKeysSize and LegalBlockSize properties of the SymmetricAlgorithm class. Doing so, we found that 128 bit (and so 16 bytes) is a possible choice for both.  We will take this value.
Notice that, from what we say previously, the IV length must be the same of the block size, being the IV mixed byte by byte with the first block to encrypt.

Example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace SymatricsEncryption
{
    /// <summary>
    /// By Naushad Nandoliya
    /// </summary>
    class Program
    {
        private static readonly Random Random = new Random();
        public static byte[] GenerateSalt()
        {
            var salt = new byte[16];
            Random.NextBytes(salt);
            return salt;
        }
        public static byte[] GenerateInitializationVector(SymmetricAlgorithm algorithm)
        {
            if (algorithm == null) throw new ArgumentNullException("algorithm");

            int size = algorithm.LegalBlockSizes[0].MinSize / 8;
            var iv = new byte[size];
            Random.NextBytes(iv);
            return iv;
        }
        public static void EncryptFile(SymmetricAlgorithm algorithm, string filePath, string password, string salt, string iv, int? keySize = null, int passwordIterations = 1000)
        {
            EncryptFile(algorithm, filePath, Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytes(salt), Encoding.UTF8.GetBytes(iv), keySize, passwordIterations);
        }

        public static void EncryptFile(SymmetricAlgorithm algorithm, string filePath, byte[] password, byte[] salt, byte[] iv, int? keySize = null, int passwordIterations = 1000)
        {
            if (algorithm == null) throw new ArgumentNullException("algorithm");
            if (String.IsNullOrEmpty(filePath)) throw new ArgumentException("File path is null or empty", "filePath");
            if (!File.Exists(filePath)) throw new FileNotFoundException("File does not exist", filePath);
            if (password == null || password.Length == 0) throw new ArgumentException("Password is empty", "password");
            if (salt == null || salt.Length < 8) throw new ArgumentException("Salt is not at least eight bytes", "salt");
            if (iv == null || iv.Length < (algorithm.LegalBlockSizes[0].MinSize / 8)) throw new ArgumentException("Specified initialization vector (IV) does not match the block size for this algorithm", "iv");

            var fileBytes = File.ReadAllBytes(filePath);
            var encryptedBytes = EncryptBytes(algorithm, fileBytes, password, salt, iv, keySize, passwordIterations);

            File.WriteAllBytes(filePath, encryptedBytes);
        }

        public static string EncryptText(SymmetricAlgorithm algorithm, string text, string password, string salt, string iv, int? keySize = null, int passwordIterations = 1000)
        {
            if (algorithm == null) throw new ArgumentNullException("algorithm");
            if (String.IsNullOrEmpty(text)) throw new ArgumentException("Text is null or empty", "text");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Password is null or empty", "password");
            if (String.IsNullOrEmpty(salt) || salt.Length < 8) throw new ArgumentException("Salt is not at least eight bytes", "salt");
            if (String.IsNullOrEmpty(iv) || iv.Length < (algorithm.LegalBlockSizes[0].MinSize / 8)) throw new ArgumentException("Specified initialization vector (IV) does not match the block size for this algorithm", "iv");

            var encryptedBytes = EncryptBytes(algorithm, Encoding.UTF8.GetBytes(text), Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytes(salt), Encoding.UTF8.GetBytes(iv), keySize, passwordIterations);
            return Convert.ToBase64String(encryptedBytes);
        }

        public static byte[] EncryptBytes(SymmetricAlgorithm algorithm, byte[] data, byte[] password, byte[] salt, byte[] iv, int? keySize = null, int passwordIterations = 1000)
        {
            if (algorithm == null) throw new ArgumentNullException("algorithm");
            if (data == null || data.Length == 0) throw new ArgumentException("Data are empty", "data");
            if (password == null || password.Length == 0) throw new ArgumentException("Password is empty", "password");
            if (salt == null || salt.Length < 8) throw new ArgumentException("Salt is not at least eight bytes", "salt");
            if (iv == null || iv.Length < (algorithm.LegalBlockSizes[0].MinSize / 8)) throw new ArgumentException("Specified initialization vector (IV) does not match the block size for this algorithm", "iv");
            if (keySize == null) keySize = algorithm.LegalKeySizes[0].MaxSize;

            byte[] keyBytes;
            using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, passwordIterations))
            {
                keyBytes = rfc2898DeriveBytes.GetBytes(keySize.Value / 8);
            }

            byte[] encrypted;
            using (var encryptor = algorithm.CreateEncryptor(keyBytes, iv))
            {
                using (var memoryStream = new MemoryStream())
                {
                    using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                    {
                        cryptoStream.Write(data, 0, data.Length);
                        cryptoStream.FlushFinalBlock();

                        encrypted = memoryStream.ToArray();

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

            return encrypted;
        }

        public static void DecryptFile(SymmetricAlgorithm algorithm, string filePath, string password, string salt, string iv, int? keySize = null, int passwordIterations = 1000)
        {
            DecryptFile(algorithm, filePath, Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytes(salt), Encoding.UTF8.GetBytes(iv), keySize, passwordIterations);
        }

        public static void DecryptFile(SymmetricAlgorithm algorithm, string filePath, byte[] password, byte[] salt, byte[] iv, int? keySize = null, int passwordIterations = 1000)
        {
            if (algorithm == null) throw new ArgumentNullException("algorithm");
            if (String.IsNullOrEmpty(filePath)) throw new ArgumentException("File path is null or empty", "filePath");
            if (!File.Exists(filePath)) throw new FileNotFoundException("File does not exist", filePath);
            if (password == null || password.Length == 0) throw new ArgumentException("Password is empty", "password");
            if (salt == null || salt.Length < 8) throw new ArgumentException("Salt is not at least eight bytes", "salt");
            if (iv == null || iv.Length < (algorithm.LegalBlockSizes[0].MinSize / 8)) throw new ArgumentException("Specified initialization vector (IV) does not match the block size for this algorithm", "iv");

            var fileBytes = File.ReadAllBytes(filePath);
            var decryptedBytes = DecryptBytes(algorithm, fileBytes, password, salt, iv, keySize, passwordIterations);

            File.WriteAllBytes(filePath, decryptedBytes);
        }

        public static string DecryptText(SymmetricAlgorithm algorithm, string encryptedText, string password, string salt, string iv, int? keySize = null, int passwordIterations = 1000)
        {
            if (algorithm == null) throw new ArgumentNullException("algorithm");
            if (String.IsNullOrEmpty(encryptedText)) throw new ArgumentException("Encrypted text are empty", "encryptedText");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Password is null or empty", "password");
            if (String.IsNullOrEmpty(salt) || salt.Length < 8) throw new ArgumentException("Salt is not at least eight bytes", "salt");
            if (String.IsNullOrEmpty(iv) || iv.Length < (algorithm.LegalBlockSizes[0].MinSize / 8)) throw new ArgumentException("Specified initialization vector (IV) does not match the block size for this algorithm", "iv");

            var decrypted = DecryptBytes(algorithm, Convert.FromBase64String(encryptedText), Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytes(salt), Encoding.UTF8.GetBytes(iv), keySize, passwordIterations);
            return Encoding.UTF8.GetString(decrypted);
        }

        public static byte[] DecryptBytes(SymmetricAlgorithm algorithm, byte[] encryptedData, byte[] password, byte[] salt, byte[] iv, int? keySize = null, int passwordIterations = 1000)
        {
            if (algorithm == null) throw new ArgumentNullException("algorithm");
            if (encryptedData == null || encryptedData.Length == 0) throw new ArgumentException("Encrypted data is null or empty", "encryptedData");
            if (password == null || password.Length == 0) throw new ArgumentException("Password is null or empty", "password");
            if (salt == null || salt.Length < 8) throw new ArgumentException("Salt is not at least eight bytes", "salt");
            if (iv == null || iv.Length < (algorithm.LegalBlockSizes[0].MinSize / 8)) throw new ArgumentException("Specified initialization vector (IV) does not match the block size for this algorithm", "iv");
            if (keySize == null) keySize = algorithm.LegalKeySizes[0].MaxSize;

            byte[] keyBytes;
            using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, passwordIterations))
            {
                keyBytes = rfc2898DeriveBytes.GetBytes(keySize.Value / 8);
            }

            byte[] plainTextBytes;
            int decryptedBytesCount;
            using (var decryptor = algorithm.CreateDecryptor(keyBytes, iv))
            {
                using (var memoryStream = new MemoryStream(encryptedData))
                {
                    using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                    {
                        plainTextBytes = new byte[encryptedData.Length];
                        decryptedBytesCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);

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

            return plainTextBytes.Take(decryptedBytesCount).ToArray();
        }
        static void Main(string[] args)
        {
            // encrypting/decrypting text
            string text = "text for encryption";
            string password = "my secret password";
            string salt = "w9=z4]0h"; // at least 8 chars long
            string iv = "fd98w7z3yupz0q41"; // have to match the algorithm block size - for all cases can be 16 chars

            using (var aes = Aes.Create())
            {
                string encrypted = Program.EncryptText(aes, text, password, salt, iv);
                string decrypted = Program.DecryptText(aes, encrypted, password, salt, iv);
                Console.WriteLine("Aes encrypted: {0}", encrypted);
                Console.WriteLine("Aes decrypted: {0}", decrypted);
            }

            Console.WriteLine();

            using (var des = DES.Create())
            {
                string encrypted = Program.EncryptText(des, text, password, salt, iv);
                string decrypted = Program.DecryptText(des, encrypted, password, salt, iv);
                Console.WriteLine("DES encrypted: {0}", encrypted);
                Console.WriteLine("DES decrypted: {0}", decrypted);
            }

            Console.WriteLine();

            using (var rc2 = RC2.Create())
            {
                string encrypted = Program.EncryptText(rc2, text, password, salt, iv);
                string decrypted = Program.DecryptText(rc2, encrypted, password, salt, iv);
                Console.WriteLine("RC2 encrypted: {0}", encrypted);
                Console.WriteLine("RC2 decrypted: {0}", decrypted);
            }

            Console.WriteLine();

            using (var rijndael = Rijndael.Create())
            {
                string encrypted = Program.EncryptText(rijndael, text, password, salt, iv);
                string decrypted = Program.DecryptText(rijndael, encrypted, password, salt, iv);
                Console.WriteLine("Rijndael encrypted: {0}", encrypted);
                Console.WriteLine("Rijndael decrypted: {0}", decrypted);
            }

            Console.WriteLine();

            using (var tripleDES = TripleDES.Create())
            {
                string encrypted = Program.EncryptText(tripleDES, text, password, salt, iv);
                string decrypted = Program.DecryptText(tripleDES, encrypted, password, salt, iv);
                Console.WriteLine("Triple DES encrypted: {0}", encrypted);
                Console.WriteLine("Triple DES decrypted: {0}", decrypted);
            }


            // encrypting/descrypting files
            using (var rijndael = Rijndael.Create())
            {
                string path = @"C:\Folder\Penguins.jpg";
                //Program.EncryptFile(rijndael, path, password, salt, iv);
                Program.DecryptFile(rijndael, path, password, salt, iv);
            }
            Console.ReadLine();
        }
    }
}
       

source by www.simple-talk.com

No comments:

Post a Comment

The Future of Remote Work, According to Startups

  The Future of Remote Work, According to Startups No matter where in the world you log in from—Silicon Valley, London, and beyond—COVID-19 ...