Return to HomePage




Encrypt a file via a block cipher using AES (C#)



Applies to

* .NET Framework 2.0
* C#


Summary

The purpose of this code is to demonstrate the proper use of a symmetric block cipher. This test case utilizes AES (Rijndael) which serves as the current NIST encryption standard.


Objectives

* To provide confidentiality of files which are transmitted over insecure or untrusted communication channels.
* To provide confidentiality of files in storage until they are needed by a user or application


Scenarios

* Application developer desires confidentiality for files in storage or in transit.


Solution Example


		        void [EncryptFile(string] [sInputFilename,] string [sOutputFilename,] string sKey, string sIV)
		        {
		            // The default AES key size under the .NET framework is 256.  The following
		            // call will create an AES crypto provider and create a random initialization
		            // vector and key. The crypto mode defaults to CBC ensuring the proper chaining 
		            // of data to mitigate repetition of cipher text blocks.
	

		            Rijndael rijndaelAlg = Rijndael.Create();
	

		            // One could override the default randomly generated key and initialization vector
		            // by setting the following properties to the Rijndael Crypto Provider, e.g.:
		            //
		            //  rijndaelAlg.Key = some_key_val;
		            //  rijndaelAlg.IV = some_iv_val;
		            //  [rijndaelAlg.KeySize] = 256; // other valid key sizes: 128 or 192 (defaults to 256)
		            // 
		            // Similarly a developer may wish to change the default encryption mode, e.g.:
		            //
		            //  rijndaelAlg.Mode = [CipherMode.CFB;] // Defaults to CBC mode. Use of ECB mode is not recommended
		                                                   // as it provides no feedback chaining of ciphertext
	

		            //Set secret key For AES algorithm.
		            rijndaelAlg.Key = [ASCIIEncoding.ASCII.GetBytes(sKey);]
	

		            //Set initialization vector.
		            rijndaelAlg.IV = [ASCIIEncoding.ASCII.GetBytes(sIV);]
	

		            [FileStream] fsPlaintext = new [FileStream(sInputFilename,] [FileMode.Open,] [FileAccess.Read);]
		            [FileStream] fsCiphertext = new [FileStream(sOutputFilename,] [FileMode.Create,] [FileAccess.Write);]
	

		            //Create an AES encryptor from the AES instance.
		            [ICryptoTransform] aesencrypt = [rijndaelAlg.CreateEncryptor();]
	

		            //Create crypto stream set to read and do an AES encryption transform on incoming bytes.
		            [CryptoStream] cipherstream = new [CryptoStream(fsCiphertext,] aesencrypt, [CryptoStreamMode.Write);]
	

		            byte[] bytearrayinput = new byte[fsPlaintext.Length];
		            fsPlaintext.Read(bytearrayinput, 0, bytearrayinput.Length);
		            cipherstream.Write(bytearrayinput, 0, bytearrayinput.Length);
		            cipherstream.Close();
		            fsPlaintext.Close();
		            fsCiphertext.Close();
		        }
	



Problem Example

The following example demonstrates an implementation of a Encryption routine containing several common secure coding issues.

		        private static void [ProblematicFileEncrypt(string] [sInputFilename,] string [sOutputFilename)]
		        {
		            Rijndael rijndaelAlg = Rijndael.Create();
	

		            //Set secret key for the AES algorithm.
		            rijndaelAlg.Key = ASCIIEncoding.ASCII.GetBytes("_A_SAMPLE_KEY_WE_USE_TO_ENCRYPT_");
		            //We don't use an IV because the book doesn't show us an example using a 
		            //more secure, chained mode! If the book told us to jump off a bridge we probably would.
		            // rijndaelAlg.IV = ASCIIEncoding.ASCII.GetBytes("SAMPLEIV");
	

		            //Set encryption mode to an unchained value (because the book says so!)
		            rijndaelAlg.Mode = [CipherMode.ECB;]
	

		            //Create a file stream to read the plaintext contents in.
		            [FileStream] fsPlaintext = new [FileStream(sInputFilename,] [FileMode.Open,] [FileAccess.Read);]
		            [FileStream] fsCiphertext = new [FileStream(sOutputFilename,] [FileMode.Create,] [FileAccess.Write);]
	

		            //Create an AES encryptor
		            [ICryptoTransform] aesencrypt = [rijndaelAlg.CreateEncryptor();]
	

		            //Create crypto stream set to read and do an AES encryption transform on incoming bytes.
		            [CryptoStream] cipherstream = new [CryptoStream(fsCiphertext,] aesencrypt, [CryptoStreamMode.Write);]
	

		            //Write the contents of the encrypted file.
		            byte[] bytearrayinput = new byte[fsPlaintext.Length];
		            fsPlaintext.Read(bytearrayinput, 0, bytearrayinput.Length);
		            cipherstream.Write(bytearrayinput, 0, bytearrayinput.Length);
		            cipherstream.Close();
		            fsPlaintext.Close();
		            fsCiphertext.Close();
		        }
	


* Cleartext encryption key can be extracted by any user with access to the source code or compiled code
* Use of a weak encryption (non-random) key, which can be brute-forced using a dictionary attack * Insecure cipher mode (ECB), performs encryption of each blocks 128 bits independently with no feedback into subsequent blocks as CBC and other chained modes do. An attacker can rearrange or replace blocks with previous blocks they observe.
* ECB mode does not utilize an initialization vector, yielding the same encrypted value for each plaintext value. An attacker can thus observe patterns in the encrypted blocks and perform known plaintext attacks (Note: Initialization vectors need not remain secret however they should be random for each encrypted value of the plaintext)

Test Case

The following classes must be included in any project making use of the sample code provided above:

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

The following test case demonstrates the use of the EncryptFile function

		        static void Main(string[] args)
		        {
		            Console.Out.WriteLine("Encrypting: " + args[0] + " to file: " + args[1]);
		            EncryptFile(args[0], args[1], sKey, sIV);
	

		            Console.Out.WriteLine("Decrypting: " + args[1] + " to file: " + args[0] + ".new");
		            DecryptFile(args[1], args[0] + ".new", sKey, sIV);
		        }
	

* sKey and sIV are retrieved from the DataProtection API within Application code

Expected Result

Comparing the Decrypted file value with the original file text should yield the same text.



More Information

* Although AES defaults to 256-bit key sizes under the .NET framework, even under current cryptography standards, the default 128-bit key-size used by many crypto implementations is still considered secure.
* The test case assumes use of the DataProtection API implemented within the user code to securely store and retrieve encryption keys.


Additional Resources

* Protecting keys using the DPAPI: http://msdn.microsoft.com/library/en-us/dnpag2/html/paght000005.asp
* Cryptography (.NET): http://msdn.microsoft.com/library/en-us/dnpag2/html/PAGGuidelines0003.asp?frame=true#pagguidelines0003_cryptography


Attributes

* Applies To: .NET Framework 2.0, C#
* Category: Cryptography
* Author: George Gal



Return to HomePage
Microsoft Communities