Recipe 14.4 Cleaning Up Cryptography Information
Problem
You will
be using the cryptography classes in the FCL to encrypt and/or
decrypt data. In doing so, you want to make sure that no data (e.g.,
seed values or keys) is left in memory for longer than you are using
the cryptography classes. Hackers can sometimes find this information
in memory and use it to break your encryption; or worse, to break
your encryption, modify the data, and then re-encrypt the data and
pass it on to your application.
Solution
In order to clear out the
key and initialization vector (or seed), we need to call the
Clear method on whichever
SymmetricAlgorithm derived or
AsymmetricAlgorithm derived class we are using.
Clear reinitializes the Key and
IV properties preventing them from being found in
memory. This is done after saving the key and IV so that we can
decrypt later. The following example shows a series of actions that
encodes a string and uses this approach to clean up immediately after
the encryption is performed to provide the smallest window possible
for potential attackers:
using System;
using System.Text;
using System.IO;
using System.Security.Cryptography;
string originalStr = "SuperSecret information";
// Encode data string to be stored in memory
byte[] originalStrAsBytes = Encoding.ASCII.GetBytes(originalStr);
byte[] originalBytes = {};
// create MemoryStream to contain output
MemoryStream memStream = new MemoryStream(originalStrAsBytes.Length);
RijndaelManaged rijndael = new RijndaelManaged( );
// generate secret key and init vector
rijndael.KeySize = 256;
rijndael.GenerateKey( );
rijndael.GenerateIV( );
// save the key and IV for later decryption
byte [] key = rijndael.Key;
byte [] IV = rijndael.IV;
// create encryptor, and stream objects
ICryptoTransform transform = rijndael.CreateEncryptor(rijndael.Key,
rijndael.IV);
CryptoStream cryptoStream = new CryptoStream(memStream, transform,
CryptoStreamMode.Write);
// write encrypted data to the MemoryStream
cryptoStream.Write(originalStrAsBytes, 0, originalStrAsBytes.Length);
cryptoStream.FlushFinalBlock( );
// release all resources as soon as we are done with them
// to prevent retaining any information in memory
memStream.Close( );
memStream = null;
cryptoStream.Close( );
cryptoStream = null;
transform.Dispose( );
transform = null;
// this clear statement regens both the key and the init vector so that
// what is left in memory is no longer the values you used to encrypt with
rijndael.Clear( );
// make this eligible for GC as soon as possible
rijndael = null;
Discussion
To be on
the safe side, we also close the MemoryStream and
CryptoStream objects as soon as possible, as well
as calling Dispose on the
ICryptoTransform implementation to clear out any
resources used in this encryption. Finally, we set the references for
all of the objects involved to null to allow the
garbage collector to collect them as soon as possible.
See Also
See the "SymmetricAlgorithm.Clear
Method" and
"AsymmetricAlgorithm.Clear Method"
topics in the MSDN documentation.
|