Return to
HomePage
Decrypt a string 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 to decrypt strings of text within an application. This test case utilizes AES (Rijndael) which serves as the current NIST encryption standard.
Objectives
* To provide confidentiality of data which is transmitted over insecure or untrusted communication channels.
* To provide confidentiality of data in storage until it is needed by a user or application
Scenarios
* Application developer desires confidentiality for data in storage or in transit.
Solution Example
public static string [DecryptString(string] ciphertext, byte[] sKey, byte[] 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();
//Set secret key For AES algorithm.
rijndaelAlg.Key = sKey;
//Set initialization vector.
rijndaelAlg.IV = sIV;
//Create a memorystream to which we'll decrypt our input string
[MemoryStream] ms = new [MemoryStream();]
[CryptoStream] ecs = new [CryptoStream(ms,] [rijndaelAlg.CreateDecryptor(),] [CryptoStreamMode.Write);]
//Because the input string is passed in as a Base64 encoded value we decode prior writing to
//the decryptor stream.
[ecs.Write(Convert.FromBase64String(ciphertext),] 0, [Convert.FromBase64String(ciphertext).Length);]
ecs.Close();
return [ASCIIEncoding.ASCII.GetString(ms.ToArray());]
}
Problem Example
The following example demonstrates an implementation of a Decryption routine containing several common
secure coding issues.
public static string [ProblematicDecryptString(string] ciphertext)
{
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 memorystream to which we'll decrypt our input string
[MemoryStream] ms = new [MemoryStream();]
[CryptoStream] ecs = new [CryptoStream(ms,] [rijndaelAlg.CreateDecryptor(),] [CryptoStreamMode.Write);]
//Because the input string is passed in as a Base64 encoded value we decode prior writing to
//the decryptor stream.
[ecs.Write(Convert.FromBase64String(ciphertext),] 0, [Convert.FromBase64String(ciphertext).Length);]
ecs.Close();
return [ASCIIEncoding.ASCII.GetString(ms.ToArray());]
}
* 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
DecryptString function
static void Main(string[] args)
{
string enc, p2, probenc, probp2 = null;
Rijndael aes = Rijndael.Create();
// Test case for string encryption routines
string p1 = "the quick brown fox jumped over the lazy dog";
// Run our test case multiple times to demonstrate different IV / encrypted text for each
// iteration in when using proper crypto modes.
Console.Out.WriteLine("Solution test cases - notice varied output for static input");
Console.Out.WriteLine("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-");
for (int i = 0; i < 5; i++)
{
aes.GenerateIV();
enc = [EncryptString(p1,] aes.Key, aes.IV);
Console.WriteLine("Encrypted text #{0}: " + enc, i);
p2 = [DecryptString(enc,] aes.Key, aes.IV);
Console.WriteLine("Plaintext #{0}: " + p2, i);
if (!p1.Equals(p2))
{
Console.Error.WriteLine("Decryption failed");
}
}
// Now run out problem test cases
Console.Out.WriteLine("Problem test cases - notice repetitive encrypted value");
Console.Out.WriteLine("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-");
for (int i = 0; i < 5; i++)
{
probenc = [ProblematicEncryptString(p1);]
probp2 = [ProblematicDecryptString(probenc);]
Console.WriteLine("Encrypted text #{0}: " + probenc, i);
Console.WriteLine("Plaintext #{0}: " + probp2, i);
}
}
* In this test case AES encryption keys and initialization vectors are chosen at random during the call to Rijndael.Create().
* In a real-world implementation sKey and sIV should be retrieved from the Data Protection API within application code please refer to code sample demonstrating use of Data Protection API.
Expected Result
Solution test cases - notice varied output for static input:
Encrypted text #0: PSQ7SRIFtv3W1GzSdsMCWR6ANZVZ78xjMG2ssQpsfyiWDMY3Wf+15Icd4XDEKhe8
Plaintext #0: the quick brown fox jumped over the lazy dog
Encrypted text #1: feBRJPILiia74FQnOpXlpIICYmieLbujF11yTkQyNitUPl35qf0aVd+VjYxyXVCL
Plaintext #1: the quick brown fox jumped over the lazy dog
Encrypted text #2: DfnDt3dO6eq3tdp3OC/5/zs6GN8jm2SUSqy50d1cwsHmQ4R+XsEttzT/7wlXXwuc
Plaintext #2: the quick brown fox jumped over the lazy dog
Encrypted text #3:
lbU6t2jc5eVNBZEr6jQDEpj4/vfrjcqke/94X6pY8mEBlYJ4qyzUJEtFQf96Cm5W Plaintext #3: the quick brown fox jumped over the lazy dog
Encrypted text #4: /by8DZLmxBpkP5fEbGw8UKXz6bmNa2IPedCgpcSdO65uNAf+ujJ2FV2EsZaTg19A
Plaintext #4: the quick brown fox jumped over the lazy dog
Problem test cases - notice repetitive encrypted value:
Encrypted text #0: S/PCsy2/87IRuRzAjeHxXw3XUEfhwZ9Ol1Bai1Hy7RqFq9+V6ZIEHlo1IwRQjCVT
Plaintext #0: the quick brown fox jumped over the lazy dog
Encrypted text #1: S/PCsy2/87IRuRzAjeHxXw3XUEfhwZ9Ol1Bai1Hy7RqFq9+V6ZIEHlo1IwRQjCVT
Plaintext #1: the quick brown fox jumped over the lazy dog
Encrypted text #2: S/PCsy2/87IRuRzAjeHxXw3XUEfhwZ9Ol1Bai1Hy7RqFq9+V6ZIEHlo1IwRQjCVT
Plaintext #2: the quick brown fox jumped over the lazy dog
Encrypted text #3: S/PCsy2/87IRuRzAjeHxXw3XUEfhwZ9Ol1Bai1Hy7RqFq9+V6ZIEHlo1IwRQjCVT
Plaintext #3: the quick brown fox jumped over the lazy dog
Encrypted text #4: S/PCsy2/87IRuRzAjeHxXw3XUEfhwZ9Ol1Bai1Hy7RqFq9+V6ZIEHlo1IwRQjCVT
Plaintext #4: the quick brown fox jumped over the lazy dog
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