4. Encrypting and Decrypting Data
The SymmetricAlgorithm class delegates the
process of encrypting and decrypting data to the
ICryptoTransform interface, which exposes the details
of handling data in blocks.
An instance of ICryptoTransform transforms
plaintext to ciphertext or transforms ciphertext
to plaintext; each ICryptoTransform is
"one-way" and can be used only for
the purpose for which it was created. The following statements
demonstrate how to create transformations, using the
CreateEncryptor and
CreateDecryptor methods:
# C#
// create the encryption algorithm
SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
// create an ICryptoTransform that can be used to encrypt data
ICryptoTransform x_encryptor = x_alg.CreateEncryptor( );
// create an ICryptoTransform that can be used to decrypt data
ICryptoTransform x_decryptor = x_alg.CreateDecryptor( );
# Visual Basic .NET
' create the encryption algorithm
Dim x_alg As SymmetricAlgorithm = SymmetricAlgorithm.Create("Rijndael")
' create an ICryptoTransform that can be used to encrypt data
Dim x_encryptor As ICryptoTransform = x_alg.CreateEncryptor( )
' create an ICryptoTransform that can be used to decrypt data
Dim x_decryptor As ICryptoTransform = x_alg.CreateDecryptor( )
The transformations that these statements create will transform data
using the key and IV from the SymmetricAlgorithm
instance; there are overloaded forms of the
CreateEncryptor and
CreateDecryptor classes that accept byte arrays to
specify particular key and IV values. Table 6
summarizes the members of
theICryptoTransform
interface.
Table 6. The members of the ICryptoTransform interface
Member
|
Description
|
---|
Properties
| |
InputBlockSize
|
Returns the number of bytes that the cipher function operates on
|
OutputBlockSize
|
Returns the number of bytes that the cipher function produces when
processing a data block of InputBlockSize bytes
|
CanReuseTransform
|
Indicates if a transformation can be used to process more than one
ciphertext/plaintext
|
CanTransformMultipleBlocks
|
If true, the TransformBlock method can accept data
in multiples of InputBlockSize bytes
|
Methods
| |
TransformBlock
|
Transforms a region of a byte array and copies the result to a region
of an output buffer
|
TransformFinalBlock
|
Transforms the final block of data
|
Instances of the ICryptoTransform interface are
not useful on their own; the .NET Framework provides the
CryptoStream companion class, which is the basis
for using instances of ICryptoTransform.
The CryptoStream class acts as a wrapper around a stream
and automatically transforms blocks of data using an
ICryptoTransform. The
CryptoStream class transforms data read from a
stream (for example, decrypting ciphertext stored in a file) or
written to a stream (for example, encrypting programmatically
generated data and storing the result in a file).
Creating instances of CryptoStream requires a real
stream, an ICryptoTransform, and a value from the
CryptoStreamMode enumeration, which defines
whether to transform the data as it is read from the stream
(CryptoStreamMode.Read) or as it is written to the
stream (CryptoStreamMode.Write).
The CryptoStream class extends
System.IO.Stream and exposes all of the
functionality of the stream it is wrapped around; requests made to
the CryptoStream
Read or
Write methods are serviced by the underlying
stream, and then transformed using an
ICryptoTransform. Figure 2
shows how the CryptoStream class transforms data
read from a file stream.
The following statements, Example 1, demonstrate
how to use the CryptoStream class to encrypt data
as it is written to a MemoryStream, which provides
a stream that is backed by an in-memory byte array:
Example 1. using the CryptoStream class to encrypt data in a stream
# C#
using System; using System.Security.Cryptography; using System.IO; using System.Text;
class MemoryEncryptionExample {
static void Main( ) {
// define the message that we will encrypt string x_message = "Programming .NET Security";
// get the bytes representing the message byte[] x_plaintext = Encoding.Default.GetBytes(x_message);
// create the memory stream MemoryStream x_memory_stream = new MemoryStream( );
// create the encryption algorithm SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("RC2");
// create an ICryptoTransform that can be used to encrypt data ICryptoTransform x_encryptor = x_alg.CreateEncryptor( );
// create the CryptoStream that ties together the FileStream and // the ICryptoTransform CryptoStream x_cryptostream = new CryptoStream(x_memory_stream, x_encryptor, CryptoStreamMode.Write);
// write the plaintext out to the cryptostream x_cryptostream.Write(x_plaintext, 0, x_plaintext.Length);
// close the CryptoStream x_cryptostream.Close( ); // get the ciphertext from the MemoryStream byte[] x_ciphertext = x_memory_stream.ToArray( );
// print out the cipher text bytes foreach (byte b in x_ciphertext) { Console.Write("{0:X2} ", b); } } }
# Visual Basic .NET
Imports System Imports System.Security.Cryptography Imports System.IO Imports System.Text
Class MemoryEncryptionExample
Shared Sub Main( )
' define the message that we will encrypt Dim x_message As String = "Programming .NET Security"
' get the bytes representing the message Dim x_plaintext( ) As Byte = Encoding.Default.GetBytes(x_message)
' create the memory stream Dim x_memory_stream As MemoryStream = New MemoryStream( )
' create the encryption algorithm Dim x_alg As SymmetricAlgorithm = SymmetricAlgorithm.Create("RC2")
' create an ICryptoTransform that can be used to encrypt data Dim x_encryptor As ICryptoTransform = x_alg.CreateEncryptor( )
' create the CryptoStream that ties together the FileStream and ' the ICryptoTransform Dim x_cryptostream As CryptoStream = New CryptoStream(x_memory_stream, _ x_encryptor, _ CryptoStreamMode.Write)
' write the plaintext out to the cryptostream x_cryptostream.Write(x_plaintext, 0, x_plaintext.Length)
' close the CryptoStream x_cryptostream.Close( )
' get the ciphertext from the MemoryStream Dim x_ciphertext( ) As Byte = x_memory_stream.ToArray( )
' print out the cipher text bytes Dim b As Byte For Each b In x_ciphertext Console.Write("{0:X2} ", b) Next End Sub End Class
|
This example provides the general model for encrypting and decrypting
data using symmetric algorithms; substitute any stream in the example
to write encrypted data using the CryptoStream
class. Note that the .NET symmetric encryption support makes no
special provision for handling data held in memory; the
MemoryStream class provides the means to process
in-memory data using streams.