[ Team LiB ] Previous Section Next Section

14.3 Programming Symmetrical Encryption

The .NET Framework takes the same basic approach to defining symmetric algorithms as the one you saw in Chapter 13 for hashing algorithms; abstract classes extend the System.Security.Cryptography.SymmetricAlgorithm class for each of the supported algorithms. Individual implementations of the algorithms extend the abstract class, supporting the possibility of more than one implementation of an algorithm, as represented by Figure 14-11.

Figure 14-11. The .NET Framework class hierarchy for symmetric encryption algorithms
figs/pdns_1411.gif

14.3.1 The SymmetricAlgorithm Class

The SymmetricAlgorithm class allows you to configure an algorithm (select the block size, padding mode, etc.) and create instances of the classes that encrypt and decrypt data; this class, and the derived implementation classes, are not used to process data directly; see Section 14.3.4 for more details. Table 14-2 summarizes the public members of the SymmetricAlgorithm class.

Table 14-2. Members of the SymmetricAlgorithm Class

Member

Description

Properties

 

BlockSize

Gets or sets the block size used by the cipher function.

FeedbackSize

Gets or sets the block size used to create feedback when encrypting data.

KeySize

Gets or sets the size in bits of the secret key used by the algorithm.

IV

Get and set the values of the secret key and initialization vector, expressed as an array of bytes.

Key

 

LegalBlockSizes

Return the range of block and secret key sizes that the algorithm supports.

LegalKeySizes

 

Mode

Gets and sets the cipher mode used to prepare data.

Padding

Gets or sets the padding mode that will fill out partial blocks of data.

Methods

 

Create

Creates a new instance of the SymmetricAlgorithm class by name. See the following section for further details.

CreateEncryptor

Create instances of the classes used to encrypt and decrypt data. See Section 14.3.4 for details.

CreateDecryptor

 

GenerateIV

Generate random secret keys and initialization vectors.

GenerateKey

 

ValidKeySize

Determines if a key of a given length is valid for the algorithm.

14.3.2 Instantiating the Algorithm

You can instantiate the implementation classes for symmetric algorithms in the same way as the hashing algorithms we examined in Chapter 13. The preferred way to create an instance is by using the Create method of the SymmetricAlgorithm class:

# C#

SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("RC2");

# Visual Basic .NET

Dim x_alg As SymmetricAlgorithm = SymmetricAlgorithm.Create("RC2")

The Create method instantiates an implementation class based on the value of the argument; Table 14-3 shows the list of supported argument strings and the implementation classes that they create. If no argument is supplied, the Create method will instantiate the RijndaelManaged class and return null (C#) or Nothing (Visual Basic .NET) if the argument is not one of those listed in Table 14-3. The system administrator configures the mapping between the string values and the instantiated classes.

Algorithms can be instantiated directly, an approach that is useful if you wish to ensure that a specific implementation class is used or if you are using algorithms for which no mappings are available:

# C#

SymmetricAlgorithm x_alg = new RC2CryptoServiceProvider(  );

# Visual Basic .NET

Dim x_alg As SymmetricAlgorithm = New RC2CryptoServiceProvider(  )

Table 14-3. Mapping string values to algorithm classes

String value

Algorithm-implementation class

DES

DESCryptoServiceProvider

System.Security.Cryptography.DES

DESCryptoServiceProvider

3DES

TripleDESCryptoServiceProvider

TripleDES

TripleDESCryptoServiceProvider

Triple DES

TripleDESCryptoServiceProvider

System.Security.Cryptography.TripleDES

TripleDESCryptoServiceProvider

RC2

RC2CryptoServiceProvider

System.Security.Cryptography.RC2

RC2CryptoServiceProvider

Rijndael

RijndaelManaged

System.Security.Cryptography.Rijndael

RijndaelManaged

14.3.3 Configuring the Algorithm

The public properties of the SymmetricAlgorithm class provide a mechanism that allows you to configure an algorithm before data is encrypted or decrypted. The following sections describe the configuration options and the impact that each option has.

14.3.3.1 Block and key sizes

The KeySize and BlockSize properties represent the size (in bits) of the secret key and the size of block processed by the cipher function. Inspect and change the current settings as follows:

# C#

SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");

// print out the current values
Console.WriteLine("Block Size: {0}", x_alg.BlockSize);
Console.WriteLine("Key Size:   {0}", x_alg.KeySize);

// change the values
x_alg.BlockSize = 192;
x_alg.KeySize   = 128;

# Visual Basic .NET

Dim x_alg As SymmetricAlgorithm = SymmetricAlgorithm.Create("Rijndael")

' print out the current values
Console.WriteLine("Block Size: {0}", x_alg.BlockSize)
Console.WriteLine("Key Size:   {0}", x_alg.KeySize)

' change the values
x_alg.BlockSize = 192
x_alg.KeySize   = 128

Each algorithm supports a different set of block and key sizes; refer to Table 14-1 for a summary of the sizes supported by the algorithms included in the .NET Framework. Inspect the valid sizes programmatically using the LegalKeySizes and LegalBlockSizes properties, which rely on the KeySizes structure, summarized in Table 14-4.

Table 14-4. The KeySizes structure

Property

Description

MinSize

The smallest valid key size in bits

SkipSize

The interval between valid key sizes in bits

MaxSize

The largest valid key size in bits

Both properties return an array of the KeySizes structure, where each element represents a range of keys. To determine the valid key lengths, begin with the smallest valid size and append multiples of the skip size until you reach the largest valid size; for example, if MinSize is 16 bits, SkipSize is 8 bits, and MaxSize is 32 bits, then the valid key lengths would be 16, 24, and 32 bits. The following code demonstrates how to determine all of the valid key lengths for a SymmetricAlgorithm instance:

# C#

// create the encryption algorithm instance
SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");

// get the valid sizes for the secret key
KeySizes[] x_size_ranges = x_alg.LegalKeySizes;

// iterate through each KeyRange in the array
foreach (KeySizes x_range in x_size_ranges) {
    // only iterate through if there is a range to process
    if (x_range.SkipSize > 0) {
        // calculate each valid size
        for (int i = x_range.MinSize; i <= x_range.MaxSize; i+= x_range.SkipSize) {
            Console.WriteLine("Valid Secret Key Size: {0}", i);
        }
    } else {
        // there is no range - only a single value
        Console.WriteLine("Valid Secret Key Size: {0}", x_range.MinSize);
    }
}

# Visual Basic .NET

' create the encryption algorithm instance
Dim x_alg As SymmetricAlgorithm = SymmetricAlgorithm.Create("Rijndael")

' get the valid sizes for the secret key
Dim x_size_ranges(  ) As KeySizes = x_alg.LegalKeySizes

' iterate through each KeyRange in the array
Dim x_range As KeySizes
For Each x_range In x_size_ranges
    ' only iterate through if there is a range to process
    If x_range.SkipSize > 0 Then
        ' calculate each valid size
        Dim i As Integer
        For i = x_range.MinSize To x_range.MaxSize Step x_range.SkipSize
            Console.WriteLine("Valid Secret Key Size: {0}", i)
        Next
    Else
        ' there is no range - only a single value
        Console.WriteLine("Valid Secret Key Size: {0}", x_range.MinSize)
    End If
Next
14.3.3.2 Cipher and padding modes

The .NET Framework supports the two padding modes that we described in Section 14.2.3 earlier in the chapter. A member of the System.Security.Cryptography.PaddingMode enumeration, as summarized in Table 14-5, represents each padding mode. The Padding property of the SymmetricAlgorithm class allows the padding mode to be determined and changed.

Table 14-5. The members of the PaddingMode enumeration

Member

Description

PKCS7

Represents the PKCS #7 padding style, where the value of the padding bytes is the total number of padding bytes added to the partial data block

Zeros

Represents the use of padding bytes that are set to 0

The .NET Framework supports block cipher modes that include those we describe in the "Cipher Modes" section of this chapter, and some additional variations. The members of the System.Security.Cryptography.CipherMode enumeration, listed in Table 14-6, represent each cipher mode. The Mode property of the SymmetricAlgorithm class configures the cipher mode; note that not all encryption algorithms support all of the cipher modes.

Table 14-6. The members of the CipherMode enumeration

Member

Description

ECB

These members represent the modes described in Section 14.2.2 of this chapter.

CBC

 

CFB

 

CTS

This member represents the "Cipher Text Stealing" mode, which is a variation of the CBC mode that computes the last block of ciphertext in such a way as to ensure that the plaintext and the ciphertext are the same size.

OFB

This member represents the "Output Feedback" mode, which is a variation of the CFB mode, using a different technique to fill the queue.

The following code example demonstrates how to inspect the padding and cipher modes:

# C#

// create the encryption algorithm instance
SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");

// view the current settings
Console.WriteLine("Padding Mode: {0}", x_alg.Padding);
Console.WriteLine("Cipher Mode:  {0}", x_alg.Mode);

// change the padding and cipher modes
x_alg.Padding = PaddingMode.Zeros;
x_alg.Mode    = CipherMode.ECB;

# Visual Basic .NET

' create the encryption algorithm instance
Dim x_alg As SymmetricAlgorithm = SymmetricAlgorithm.Create("Rijndael")

' view the current settings
Console.WriteLine("Padding Mode: {0}", x_alg.Padding)
Console.WriteLine("Cipher Mode:  {0}", x_alg.Mode)

' change the padding and cipher modes
x_alg.Padding = PaddingMode.Zeros
x_alg.Mode = CipherMode.ECB
14.3.3.3 Keys and initialization vectors (IVs)

The .NET Framework expresses secret keys and initialization vectors (IVs) as arrays of bytes. The Key and IV properties of the SymmetricAlgorithm class allow you to get and set the values.

If you set values using the Key and IV properties, the SymmetricAlgorithm class will use them for encryption or decryption. If you wish to generate random values for the key and IV, simply get the values from the related properties, as illustrated in the following code fragment:

# C#

// create the encryption algorithm instance
SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("DES");

// we are "getting" the value of the secret key, which
// will lead the SymmetricAlgorithm class to create a
// new random key
byte[] x_secret_key = x_alg.Key;

// we are "setting" the value of the secret key, which 
// will now be used for any subsequent encryption or
// decryption operations
x_alg.Key = new byte[] {0xD0, 0x8C, 0xD3, 0xEB, 0x10, 0x60, 0x41, 0x59};

# Visual Basic .NET

' create the encryption algorithm instance
Dim x_alg As SymmetricAlgorithm = SymmetricAlgorithm.Create("DES")

' we are "getting" the value of the secret key, which
' will lead the SymmetricAlgorithm class to create a
' new random key
Dim x_secret_key(  ) As Byte = x_alg.Key

' we are "setting" the value of the secret key, which 
' will now be used for any subsequent encryption or
' decryption operations
x_alg.Key = New Byte(  ) {&HD0, &H8C, &HD3, &HEB, &H10, &H60, &H41, &H59}

You do not have to get the values of the key and IV explicitly to create random values; any encryption or decryption operation will lead to the SymetricAlgorithm implementation class creating new Key and IV values if they have not been set. If you are using randomly generated values, you must be sure to take note of the key and IV values used to encrypt the plaintext or you won't be able to decrypt the ciphertext.

14.3.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 14-7 summarizes the members of the ICryptoTransform interface.

Table 14-7. 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 14-12 shows how the CryptoStream class transforms data read from a file stream.

Figure 14-12. Using the CryptoStream class to transform data
figs/pdns_1412.gif

The following statements, Example 14-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 14-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.

    [ Team LiB ] Previous Section Next Section