Wednesday, February 25, 2015

Create Key from Password Using PBKDF2

In the previous post I explained the usage of the AES encryption. For that we created the needed key and IV manually by converting strings chosen by us into Byte arrays. This is not how it should be done. Therefore here a tutorial how to create a key and IV in a safer way.
For this we use the key derivation function PBKDF2. This is implemented in the .Net class Rfc2898DeriveBytes.
First again some theory: PBKDF2 is short for Password-Based Key Derivation Function 2. Its purpose is the creation of a byte sequence (from which then a part can be used as key / IV) out of a chosen password. The byte sequence is created after the initialization (amongst others with the password) via a pseudo random function and thus can be extended to nearly arbitrary length.
So instead of chosing a password, and then creating e.g. via System.Text.Encoding.UTF8.GetBytes() a byte sequence as key / IV directly, we let the above mentioned function do this step. What does this help us now? For this we have to take a closer look at its mode of operation.
In the PBKDF2 method the chosen password is combined with a chosen salt and then hashed multiple times. This forms the basis for the pseudorandom generator, with which the byte sequence is generated. So we must / can choose the password, the hash and the number of iterations. This has many advantages compared to the naive method, here are some of them (they are not ordered by their importance, but nearly the other way around!):

  • As already recognized in the previous post the conversion between String and Byte Array can cause problems, since the size of single characters varies and we thus have to use a string of the correct length, so that it satisfies the requirements of e.g. AES regarding key size etc. With PBKDF2 we can choose arbitrary passwords, and then simply take the needed number of bytes from the result.
  • Amongst others the hashing creates an almost equal distribution of the resulting bytes. The direct byte representation of chosen passwords (mostly words or numbers) is often predictable and thus the resulting key more vulnerable to Brute-Force attacks.
  • Multiple rounds of hashing hinder and slow down a Brute-Force attack on the key, since the attacker now not only has to guess the password but also the number of rounds n, further the hash function has to be applied n times.
  • The additional selection of a salt hinders the usage of precalculated rainbow tables.
Now to the code, the following lines calculate out of the given password and salt via the PBKDF2 method with 10000 iterations a byte sequence. With the function GetBytes(m) we get the next m of the sequence (here 16 for AES 128 bit):


Rfc2898DeriveBytes Generator = new Rfc2898DeriveBytes("Any password", System.Text.Encoding.UTF8.GetBytes("some salt"), 10000);
byte[] Key = Generator.GetBytes(16);
byte[] IV = Generator.GetBytes(16);

Tuesday, February 24, 2015

AES (Rijndael) Encryption

In this post I want to explain how one can use the Rijndael encryption in C#. This is a symmetric block cipher, which was chosen after DES as encryption standard AES (Advanced Encryption Standard). A symmetric block cipher uses the same key for en- and decryption.
Different encryption algorithms are available in the class System.Security.Cryptography in .Net. The en- and decryption is then done via a CryptoStream, which en- or decrypts and which writes the result to a MemoryStream.
For the encryption we of course need a key. AES supports the key sizes 128, 192 and 256 Bit. The key is passed as a byte array, I will just take a string though and convert this via System.Text.Encoding.UTF8.GetBytes() to a byte array. This is somehow problematic, as UTF8 uses 1 - 4 bytes and no fixed size for coding a single character, depending on the character. Therefor, depending on the string the key might have an invalid size. There are better methods to create keys, but this should suffice here, since standard characters are coded with 1 byte.
Block ciphers further need an initialization vector (IV). This is due to the fact that the text is split in blocks of same size which are then encrypted separately. To make it harder to obtain information from this structure an IV is used to mask the first block and then subsequently randomize the encryption of the following ones based on this (see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation). AES supports also the block sizes 128, 192 and 256 Bit. The length of the IV has to be a block size divided by 8.
Let us take the smallest key size, 128 Bit. Suppose every character is coded by 1 Byte, then we need a string of length 16 as key. For the IV we also take a string of length 16.
Now the code, which should be relatively easy to understand in my opinion after the previous explanations:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Security.Cryptography;
using System.IO;

namespace AES
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // legal key sizes are 128, 192 or 256 bit
            // legal block sizes are 128, 192 or 256 bit
            // IV has to be of length blocksize / 8

            string Key = "1234567812345678";
            string IV = "0000000000000000";

            byte[] Ciphertext = Encode("This is a secret.", Key, IV);
            string Plaintext = Decode(Ciphertext, Key, IV);

            textBox1.Text = System.Text.Encoding.UTF8.GetString(Ciphertext);
            textBox2.Text = Plaintext;
        }

        public string Decode(byte[] encryptedBytes, string key, string IV)
        {
            Rijndael AESCrypto = Rijndael.Create();
            AESCrypto.Key = System.Text.Encoding.UTF8.GetBytes(key);
            AESCrypto.IV = System.Text.Encoding.UTF8.GetBytes(IV);

            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, AESCrypto.CreateDecryptor(), CryptoStreamMode.Write);

            cs.Write(encryptedBytes, 0, encryptedBytes.Length);
            cs.Close();

            byte[] DecryptedBytes = ms.ToArray();
            return System.Text.Encoding.UTF8.GetString(DecryptedBytes);
        }

        public byte[] Encode(string plaintext, string key, string IV)
        {
            Rijndael AESCrypto = Rijndael.Create();
            AESCrypto.Key = System.Text.Encoding.UTF8.GetBytes(key);
            AESCrypto.IV = System.Text.Encoding.UTF8.GetBytes(IV);

            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, AESCrypto.CreateEncryptor(), CryptoStreamMode.Write);

            byte[] PlainBytes = System.Text.Encoding.UTF8.GetBytes(plaintext);
            cs.Write(PlainBytes, 0, PlainBytes.Length);
            cs.Close();

            byte[] EncryptedBytes = ms.ToArray();
            return EncryptedBytes;
        }

    }
}

One annotation: The function Encode() returns a byte array and no string. This is due to the fact, that the resulting bytes are displayed as special characters and thus result in different sizes when converting from and to string, creating a not readable string for the algorithm.