Giovanni Palmiotto (Montreal, Canada) is a Senior Software Developer and independent contractor. He has over 10 years of professional experience developing enterprise-level software applications. He is a Microsoft Certified Technology Specialist (MCTS) as well as a Microsoft Certified Professional (MCP). He specializes in Microsoft .NET and SQL Server 2005 technologies.
Encryption: The art and science of encoding data so that a key is required to decode and restore the original data. In this first of two articles, I will introduce and explore Symmetric Key Encryption as implemented in the Microsoft .NET Framework 2.0.
What is Symmetric Key Encryption?
Symmetric key encryption is a cryptography technique that uses a shared secret key to encrypt and decrypt data. Symmetric encryption algorithms are very efficient at processing large amounts of information and computationally less intensive than asymmetric encryption algorithms. There are two types of symmetric encryption algorithms: stream ciphers and block ciphers which provide bit-by-bit and block encryption respectively.
Disadvantages of Symmetric Encryption Algorithms
A major disadvantage of symmetric encryption algorithms is the requirement for a shared secret key which must be exchanged between two parties: the sender and the recipient. This requirement necessitates a high level of trust as the process of choosing, distributing and storing keys is difficult to achieve in a dependable and secure manner. As a result, keys should be changed on a regular basis and kept secure during distribution; this process is known as key management. Another disadvantage is that there is no provision for data origin authentication and data integrity protection. In other words, the recipient can neither authenticate the sender nor verify that the decrypted message is the same as the original message.
Symmetric Encryption Algorithm Implementations
The Microsoft .NET Framework 2.0 offers four implementations of symmetric encryption algorithms.
64 bits (although the effective key strength is 56 bits).
Data Encryption Standard (DES). Considered vulnerable and should be avoided.
RC2
RC2CryptoServiceProvider
40 bits to 1024 bits in 8 bit increments.
Replacement for DES.
Triple DES
TripleDESCryptoServiceProvider
128 bits to 192 bits in 64 bit increments.
A triple application of DES.
Rijndael
RijndaelManaged
128, 192, or 256 bits.
Also known as Advanced Encryption Standard (AES). Natively supported by .NET.
Hint: When running on Microsoft Windows XP or later, it is strongly recommended to use the Rijndael algorithm (RijndaelManaged). The Rijndael algorithm is U.S. government-approved and is considered to be the most secure. In addition, it is the only managed implementation and is natively supported by the Microsoft .NET Framework 2.0.
The SymmetricAlgorithm Class
Located in the System.Security.Cryptography namespace, the SymmetricAlgorithm class is the abstract base class from which all implementations of symmetric algorithms must inherit. Classes that derive from the SymmetricAlgorithm class use a chaining mode called Cipher Block Chaining (CBC) which requires a key (Key) and an initialization vector (IV) to perform cryptographic transformations (encryption and decryption) on data. Tables 2 and 3 denote the important SymmetricAlgorithm class properties and methods.
Releases all resources used by the SymmetricAlgorithm class.
CreateDecryptor
Overloaded. Creates a symmetric decryptor object.
CreateEncryptor
Overloaded. Creates a symmetric encryptor object.
GenerateIV
Generates a random initialization vector (IV) to use for the algorithm.
GenerateKey
Generates a random key (Key) to use for the algorithm.
ValidKeySize
Determines whether the specified key size is valid for the current algorithm.
Hint: Explicitly calling the SymmetricAlgorithm.Clear method is strongly recommended as sensitive information (Key, IV) may be present in the unallocated memory heap. Relying on the indeterminate nature of the Common Language Runtime's (CLR) garbage collection mechanism to dispose of a cryptographic object may pose a security risk.
Establishing a Symmetric Key
Symmetric encryption algorithms require keys to be of a specific size (length). Simply setting the SymmetricAlgorithm.Key property to a user-provided value will not function as expected. To help remedy this situation, the Microsoft .NET Framework 2.0 introduces the Rfc2898DeriveBytes class which implements password-based key derivation functionality (PBKDF2). Keys are derived by using a pseudo-random number generator based on HMACSHA1. The HMACSHA1 algorithm computes a Hash-based Message Authentication Code (HMAC) using the SHA1 hash function.
The Rfc2898DeriveBytes constructor accepts a password, a salt value (byte array), and an (optional) iteration count, and generates derived keys by calling the Rfc2898DeriveBytes.GetBytes method. The password, salt value and (optional) iteration count values should be randomly generated and synchronized between the sender and the recipient.
It is recommended that the newer Rfc2898DeriveBytes class be used as a replacement for the PasswordDeriveBytes class (PBKDF1) used in the Microsoft .NET Framework 1.1. Be aware that the keys generated by both classes will produce different results. Though similar in functionality and implementation, it is imperative that the Rfc2898DeriveBytes class not be used to decrypt data encoded with the PasswordDeriveBytes class and vice versa.
Hint: The SymmetricAlgorithm.KeySize and SymmetricAlgorithm.BlockSize properties are defined in bits while the Rfc2898DeriveBytes.GetBytes method requires the number of bytes. Therefore, the number of bits must be divided by 8 to obtain the number of bytes required for the Rfc2898DeriveBytes.GetBytes method.
Encrypting and Decrypting Data Using Symmetric Encryption Algorithms
Having agreed upon and exchanged a shared secret key, the sender and the recipient are able to exchange encrypted data. The steps are as follows:
Create a SymmetricAlgorithm derived object (Table 1) and specify the Key, and the IV.
Create Stream objects that will interface with the SymmetricAlgorithm object (Step 1).
Create an ICryptoTransform object by calling the SymmetricAlgorithm.CreateEncryptor method (when encrypting) or SymmetricAlgorithm.CreateDecryptor method (when decrypting).
Create a CryptoStream object using the Stream object and the ICryptoTransform object as defined in Step 2 and Step 3 respectively.
Read from or write to the CryptoStream object depending on the context of the operation.
The following code sample demonstrates an implementation of the RijndaelManaged class to encrypt files using a shared secret key:
Private Sub Encrypt(ByVal inputFile As String, ByVal outputFile As String, ByVal key As String)
Dim KeyBytes As Byte()
KeyBytes = Encoding.ASCII.GetBytes(key)
'Derive salted key.
Dim DerivedKey As Rfc2898DeriveBytes
DerivedKey = New Rfc2898DeriveBytes(key, KeyBytes)
'Create SymmetricAlgorithm object and specify the Key and IV.
Dim RijndaelCSP As RijndaelManaged
RijndaelCSP = New RijndaelManaged
RijndaelCSP.Key = DerivedKey.GetBytes(CType(RijndaelCSP.KeySize / 8, Integer))
RijndaelCSP.IV = DerivedKey.GetBytes(CType(RijndaelCSP.BlockSize / 8, Integer))
'Create an ICryptoTransform (Encryptor) object.
Dim Encryptor As ICryptoTransform
Encryptor = RijndaelCSP.CreateEncryptor
'Read the unencrypted file.
Dim InputFileStream As FileStream
InputFileStream = New FileStream(inputFile, FileMode.Open, FileAccess.Read)
Dim InputFileData(CType(InputFileStream.Length, Integer)) As Byte
InputFileStream.Read(InputFileData, 0, CType(InputFileStream.Length, Integer))
Dim OutputFileStream As FileStream
OutputFileStream = New FileStream(outputFile, FileMode.Create, FileAccess.Write)
'Create a CryptoStream object using the Stream and ICryptoTransform objects.
Dim EncryptCryptoStream As CryptoStream
EncryptCryptoStream = New CryptoStream(OutputFileStream, Encryptor, CryptoStreamMode.Write)
EncryptCryptoStream.Write(InputFileData, 0, InputFileData.Length)
EncryptCryptoStream.FlushFinalBlock()
'Clear any sensitive data from the cyptographic object.
RijndaelCSP.Clear()
'Close stream objects.
EncryptCryptoStream.Close()
InputFileStream.Close()
OutputFileStream.Close()
End Sub
Invoke the Encrypt method by specifying an input file (plaintext), an output file (encrypted), and a shared secret key:
The following code sample demonstrates an implementation of the RijndaelManaged class to decrypt files using a shared secret key:
Private Sub Decrypt(ByVal inputFile As String, ByVal outputFile As String, ByVal key As String)
Dim KeyBytes As Byte()
KeyBytes = Encoding.ASCII.GetBytes(key)
'Derive salted key.
Dim DerivedKey As Rfc2898DeriveBytes
DerivedKey = New Rfc2898DeriveBytes(key, KeyBytes)
'Create SymmetricAlgorithm object and specify the Key and IV.
Dim RijndaelCSP As RijndaelManaged
RijndaelCSP = New RijndaelManaged
RijndaelCSP.Key = DerivedKey.GetBytes(CType(RijndaelCSP.KeySize / 8, Integer))
RijndaelCSP.IV = DerivedKey.GetBytes(CType(RijndaelCSP.BlockSize / 8, Integer))
'Create an ICryptoTransform (Decryptor) object.
Dim Decryptor As ICryptoTransform
Decryptor = RijndaelCSP.CreateDecryptor
'Read the encrypted file.
Dim InputFileStream As FileStream
InputFileStream = New FileStream(inputFile, FileMode.Open, FileAccess.Read)
'Create a CryptoStream object using the Stream and ICryptoTransform objects.
Dim DecryptCryptoStream As CryptoStream
DecryptCryptoStream = New CryptoStream(InputFileStream, Decryptor, CryptoStreamMode.Read)
Dim InputFileData(CType(InputFileStream.Length, Integer)) As Byte
DecryptCryptoStream.Read(InputFileData, 0, CType(InputFileStream.Length, Integer))
Dim OutputFileStream As FileStream
OutputFileStream = New FileStream(outputFile, FileMode.Create, FileAccess.Write)
OutputFileStream.Write(InputFileData, 0, InputFileData.Length)
OutputFileStream.Flush()
'Clear any sensitive data from the cyptographic object.
RijndaelCSP.Clear()
'Close stream objects.
DecryptCryptoStream.Close()
InputFileStream.Close()
OutputFileStream.Close()
End Sub
Invoke the Decrypt method by specifying an input file (encrypted), an output file (decrypted), and a shared secret key:
Deciding what encryption algorithm is best for a given situation takes careful planning and testing. In this article, we explored symmetric key encryption through the use of the SymmetricAlgorithm class (and its derived implementations). In my forthcoming article, I will explore asymmetric (or public key) encryption as implemented in the Microsoft .NET Framework 2.0.
I sincerely hope that you have had as much fun reading this article, as I had writing it.