Encryption (part 1, basics), AES

After serializing, storing and sending data across the network, we are still working on the same subject. Cryptography does belong into that area as well.
The simplified process is:

data => password => encryption => cipher => password => decryption => data

I am going to concentrate on the well-known AES algorithm. It has a good and reliable history. You can hardly hack it with eg. three allowable attempts per five minutes. As usual I have added links to this post for further explanations. AES (Advanced Encryption Standard) is reportedly used by the U.S. government and was recommended by the U.S. National Institute of Standards and Technology (NIST).

The .Net Framework supports cryptography in the System.Security.Cryptography namespace. The AES algorithm can be found in the AesManaged class. It uses symmetric encryption; there is only one password.

Besides the password there is a so-called Initialization Vector (IV). It adds more randomness. The IV ensures that data does not result in the same cipher data. You could pretty much say that each result is unique. Repetitive patterns are much harder to find.

Users enter passwords, but the AES algorithm does not use these directly. It requires keys instead. Therefore we must translate passwords into keys.

Key sizes of 128, 160, 192, 224, and 256 bits are supported by the algorithm, but only the 128, 192, and 256-bit key sizes are specified in the AES standard.
Block sizes of 128, 160, 192, 224, and 256 bits are supported by the algorithm, but only the 128-bit block size is specified in the AES standard.

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

Some extension methods can be found in System.Linq. We use string.Join().

private static AesManaged GetAesAlgo() {
   AesManaged lAlgo = new AesManaged();
   lAlgo.BlockSize = 128;
   lAlgo.KeySize = 128;
   lAlgo.Key = ASCIIEncoding.ASCII.GetBytes("Bastian/M/K/Ohta"); // 128 bits = 16 bytes = 16 ASCII characters
   lAlgo.IV = ASCIIEncoding.ASCII.GetBytes("1234567890123456"); // 128 bits = 16 bytes = 16 ASCII characters
   Console.WriteLine("Aes block size is " + lAlgo.BlockSize + " bits");
   return lAlgo;
} //

static byte[] Encrypt(SymmetricAlgorithm xAlgo, byte[] xData) {
   ICryptoTransform lEncryptor = xAlgo.CreateEncryptor(xAlgo.Key, xAlgo.IV);
   return lEncryptor.TransformFinalBlock(xData, 0, xData.Length);
} //

static byte[] Decrypt(SymmetricAlgorithm xAlgo, byte[] xCipher) {
   ICryptoTransform lDecryptor = xAlgo.CreateDecryptor(xAlgo.Key, xAlgo.IV);
   return lDecryptor.TransformFinalBlock(xCipher, 0, xCipher.Length);
} //

public static void Test1() {
   string lText = "Let's encrypt and decrypt this text :)";
   byte[] lTextAsBytes = UnicodeEncoding.Unicode.GetBytes(lText);

   Console.WriteLine("text length in characters " + lText.Length);
   Console.WriteLine("text length in bytes " + lTextAsBytes.Length);

   using (SymmetricAlgorithm lAlgo = GetAesAlgo()) {
      byte[] lEncrypted = Encrypt(lAlgo, lTextAsBytes);
      Console.WriteLine("encrypted data size in bytes " + lTextAsBytes.Length);

      byte[] lDecrypted = Decrypt(lAlgo, lEncrypted);
      Console.WriteLine("decrypted text length in bytes " + lDecrypted.Length);

      string lResult = UnicodeEncoding.Unicode.GetString(lDecrypted);
      Console.WriteLine("text length in characters now " + lResult.Length);
      Console.WriteLine();
      Console.WriteLine("the text was: \"" + lResult + "\"");
   }
   Console.ReadLine();
} //

example output:
text length in characters 38
text length in bytes 76
Aes block size is 128 bits
encrypted data size in bytes 76
decrypted text length in bytes 76
text length in characters now 38

the text was: “Let’s encrypt and decrypt this text :)”

The above code was for warming up. It encrypts/decrypts all data in memory without the use of any stream. The next example uses the FileStream class to store a file to the desktop. Some people prefer to use the memory stream in their examples. I personally do not get the point of this as it is not a requirement. I did show above that you can convert something in memory without using the MemoryStream class.

private static AesManaged GetAesAlgo() {
   AesManaged lAlgo = new AesManaged();
   lAlgo.BlockSize = 128;
   lAlgo.KeySize = 128;
   lAlgo.Key = ASCIIEncoding.ASCII.GetBytes("Bastian/M/K/Ohta"); // 128 bits = 16 bytes = 16 ASCII characters
   lAlgo.IV = ASCIIEncoding.ASCII.GetBytes("1234567890123456"); // 128 bits = 16 bytes = 16 ASCII characters
   Console.WriteLine("Aes block size is " + lAlgo.BlockSize + " bits");
   return lAlgo;
} //

public static void Test2() {
   string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\";
   string lFileName = lDesktopPath + "Encrypted.bin";
   string lText = "Let's encrypt and decrypt this text :)";
   byte[] lTextAsBytes = UnicodeEncoding.Unicode.GetBytes(lText);

   Console.WriteLine("text length in characters " + lText.Length);
   Console.WriteLine("text length in bytes " + lTextAsBytes.Length);

   try {
      using (SymmetricAlgorithm lAlgo = GetAesAlgo()) {
         EncryptToStream(lAlgo, lFileName, lTextAsBytes);

         FileInfo lFileInfo = new FileInfo(lFileName);
         Console.WriteLine("encrypted file size in bytes " + lFileInfo.Length);

         int lLength;
         byte[] lDecrypted = DecryptFromStream(lAlgo, lFileName, out lLength);
         Console.WriteLine("decrypted text length in bytes " + lDecrypted.Length);
         //Console.WriteLine("decrypted text length in bytes " + lLength);

         string lResult = UnicodeEncoding.Unicode.GetString(lDecrypted);
         //string lResult = UnicodeEncoding.Unicode.GetString(lDecrypted, 0, lLength);

         Console.WriteLine("text length in characters now " + lResult.Length);
         Console.WriteLine();
         Console.WriteLine("text before encryption: \"" + lText + "\"");
         Console.WriteLine("text after decryption: \"" + lResult + "\"");
      }
   }
   catch (Exception ex) { Console.WriteLine(ex.Message); }
   Console.ReadLine();
} //

static void EncryptToStream(SymmetricAlgorithm xAlgo, string xFileName, byte[] xData) {
   ICryptoTransform lEncryptor = xAlgo.CreateEncryptor(xAlgo.Key, xAlgo.IV);
   System.IO.File.Delete(xFileName);
   using (FileStream lFileStream = new FileStream(xFileName, FileMode.CreateNew)) {
      using (CryptoStream lCryptoStream = new CryptoStream(lFileStream, lEncryptor, CryptoStreamMode.Write)) {
         lCryptoStream.Write(xData, 0, xData.Length);
      }
   }
} //

static byte[] DecryptFromStream(SymmetricAlgorithm xAlgo, string xFileName, out int xLength) {
   ICryptoTransform lDecryptor = xAlgo.CreateDecryptor(xAlgo.Key, xAlgo.IV);
   using (FileStream lFileStream = new FileStream(xFileName, FileMode.Open)) {
      using (CryptoStream lCryptoStream = new CryptoStream(lFileStream, lDecryptor, CryptoStreamMode.Read)) {
         int lLength = (int)lFileStream.Length;
         byte[] lResult = new byte[lLength];
         xLength = lCryptoStream.Read(lResult, 0, lLength);
         return lResult;
      }
   }
} //

example output:
text length in characters 38
text length in bytes 76
Aes block size is 128 bits
encrypted file size in bytes 80
decrypted text length in bytes 80
text length in characters now 40

text before encryption: “Let’s encrypt and decrypt this text :)”
text after decryption: “Let’s encrypt and decrypt this text 🙂 ”

I added a typical beginner’s mistake. And to point it out I printed a lot of text messages about the data sizes. Comment and uncomment the following lines to correct the mistake:

Console.WriteLine("decrypted text length in bytes " + lDecrypted.Length);
//Console.WriteLine("decrypted text length in bytes " + lLength);

string lResult = UnicodeEncoding.Unicode.GetString(lDecrypted);
//string lResult = UnicodeEncoding.Unicode.GetString(lDecrypted, 0, lLength);

CHANGE THE ABOVE LINES TO:

//Console.WriteLine("decrypted text length in bytes " + lDecrypted.Length);
Console.WriteLine("decrypted text length in bytes " + lLength);

//string lResult = UnicodeEncoding.Unicode.GetString(lDecrypted);
string lResult = UnicodeEncoding.Unicode.GetString(lDecrypted, 0, lLength);

example output:
text length in characters 38
text length in bytes 76
Aes block size is 128 bits
encrypted file size in bytes 80
decrypted text length in bytes 76
text length in characters now 38

text before encryption: “Let’s encrypt and decrypt this text :)”
text after decryption: “Let’s encrypt and decrypt this text :)”

The block size is 128 bits (=16 bytes). 76 bytes are not a multiple of 16, but 80 bytes are. This results in a larger file, which uses padding at the end. Taking the wrong size did result in a wrong decrypted text.

Usually you do not store passwords. Passwords are used as keys to decrypt data. If the password is wrong, then the decoded data is wrong. Some programmers hardcode passwords to lazily compare them against password entries. This is really bad practice!
Anyhow, some applications do store passwords to make logins easier. Eg. your internet browser has such functionality. In this case you should salt the result. Salt makes cracking passwords more time-consuming, it doesn’t stop criminals though. Nevertheless, every little helps. John Doe will not be able to use your salted password.

The good old hardcore hackers did not care much about passwords. There was no need to crack them. Hackers had freezer modules that could freeze the computer and gave access to all memory including the running assembler code. You only needed to freeze the PC when it was asking for a password. You checked where your program counter was and then did the same right after you had entered the password.
All you had to do was to add an unconditional branch command to bypass this password request. Hackers were then looking for the same code sequence on the disk and changed it accordingly. These days work a bit different.
But why am I telling this? I want to emphasize that you have to encrypt executable code as well to protect yourself efficiently. It is not enough to only encrypt data. The best code decrypts itself bit by bit at runtime.

Back to salt.
Sometimes users use the same password. When you store these passwords with individual salt values in your database, then they do look entirely different. Again, this makes John Doe’s criminal life or his simple temptation harder. A static salt value is not a good idea. You’d better store the salt value right next to your password. You can even encrypt the salt value with hardcoded algorithm parameters. This makes it a bit safer again.

public static void Test3() {

   // variable salt
   Console.WriteLine("random salt");
   string lPassword = "MySooperDooperSekr1tPa$$wörd";
   byte[] lKey, lSalt;
   GenerateSaltedKey(lPassword, out lKey, out lSalt, 128);
   Console.WriteLine("password: " + lPassword);
   Console.WriteLine("key: " + string.Join(" ", lKey));
   Console.WriteLine("salt: " + string.Join(" ", lSalt));
   Console.WriteLine();
   Console.WriteLine("saving to database");
   Console.WriteLine("[Base64String] key: " + Convert.ToBase64String(lKey));
   Console.WriteLine("[Base64String] salt: " + Convert.ToBase64String(lSalt));
   Console.WriteLine();

   // backtest
   byte[] lKey2 = GenerateKey128(lPassword, lSalt); // do we get the same key?
   Console.WriteLine("key: " + string.Join(" ", lKey));
   Console.WriteLine("key: " + string.Join(" ", lKey2));
   if (lKey.SequenceEqual(lKey2)) Console.WriteLine("keys are equal");
   else Console.WriteLine("Oh dear, something went wrong!");
   Console.WriteLine();


   // static salt
   Console.WriteLine("static salt");
   for (int i = 0; i < 10; i++) {
      byte[] lStaticKey = GenerateKey128(lPassword);
      Console.WriteLine(i + " static key " + string.Join(" ", lStaticKey));
   }

   Console.ReadLine();
} //

private const int _Iterations = 2500;

private static void GenerateSaltedKey(string xPassword, out byte[] xKey, out byte[] xSalt, int xKeySize = 256) {
   xKeySize /= 8; // now in bytes
   var keyGenerator = new Rfc2898DeriveBytes(xPassword, xKeySize, _Iterations);
   xKey = keyGenerator.GetBytes(xKeySize);
   xSalt = keyGenerator.Salt;
} //

private static byte[] GenerateKey128(string xPassword) {
   byte[] lSalt = { 252, 132, 52, 13, 64, 158, 12, 10, 50, 80, 74, 63, 15, 54, 76, 246 }; // 16 bytes == 128 bits
   return new Rfc2898DeriveBytes(xPassword, lSalt, _Iterations).GetBytes(16);
} //

private static byte[] GenerateKey128(string xPassword, byte[] xSalt) {
   return new Rfc2898DeriveBytes(xPassword, xSalt, _Iterations).GetBytes(16);
} //

example output:
random salt
password: MySooperDooperSekr1tPa$$wörd
key: 139 209 30 237 82 66 102 245 193 89 175 218 62 190 7 8
salt: 58 247 144 225 92 236 82 167 255 185 6 135 45 104 86 98

saving to database
[Base64String] key: i9Ee7VJCZvXBWa/aPr4HCA==
[Base64String] salt: OveQ4VzsUqf/uQaHLWhWYg==

key: 139 209 30 237 82 66 102 245 193 89 175 218 62 190 7 8
key: 139 209 30 237 82 66 102 245 193 89 175 218 62 190 7 8
keys are equal

static salt
0 static key 103 183 126 80 87 205 118 92 45 195 66 134 124 104 32 206
1 static key 103 183 126 80 87 205 118 92 45 195 66 134 124 104 32 206
2 static key 103 183 126 80 87 205 118 92 45 195 66 134 124 104 32 206
3 static key 103 183 126 80 87 205 118 92 45 195 66 134 124 104 32 206
4 static key 103 183 126 80 87 205 118 92 45 195 66 134 124 104 32 206
5 static key 103 183 126 80 87 205 118 92 45 195 66 134 124 104 32 206
6 static key 103 183 126 80 87 205 118 92 45 195 66 134 124 104 32 206
7 static key 103 183 126 80 87 205 118 92 45 195 66 134 124 104 32 206
8 static key 103 183 126 80 87 205 118 92 45 195 66 134 124 104 32 206
9 static key 103 183 126 80 87 205 118 92 45 195 66 134 124 104 32 206

I did set the number of iterations to 2500 (private const int _Iterations = 2500). This is an arbitrary number. A value of above 1000 is generally recommended. It tells the key/IV generator how many times it should run. Think of recursion to get an idea. Make sure you use the same number of iterations whenever you want the same results.

Advertisements

About Bastian M.K. Ohta

Happiness only real when shared.

Posted on February 4, 2014, in Basic, C#, Encryption, Network and tagged , , , , , , , , , , , , , , , , , , , , , , , . Bookmark the permalink. Leave a comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: