© Stephen Haunts 2019
Stephen HauntsApplied Cryptography in .NET and Azure Key Vaulthttps://doi.org/10.1007/978-1-4842-4375-6_6

6. Symmetric Encryption

Stephen Haunts1 
(1)
Belper, Derbyshire, UK
 

So far, we have covered integrity by looking at hashing, and authentication by looking at hashed message authentication codes. In this chapter, we explore confidentiality by looking at symmetric encryption .

First, we look at the history of the Data Encryption Standard (DES) and its immediate successor, Triple DES. While DES and Triple DES are not recommended for use in new applications, it is still necessary to discuss them because you may need to integrate with legacy systems that still rely on them. Once we cover DES and Triple DES, we take a look at their successor, the Advanced Encryption Standard.

This chapter isn’t meant to be an advanced analysis of these algorithms at an advanced mathematical level because you don’t need to understand how these algorithms work to use them entirely, but a little background information sets some useful context when using them in your applications. Once you have foundational knowledge of how these algorithms work, we look at their implementations within the .NET Framework and .NET Core.

Symmetric Encryption

In the last chapter, we discussed how hashing and hashed message authentication codes are one-way operations. Once you hash some data, you shouldn’t be able to reverse the hash to go back to the original data. Symmetric encryption algorithms, on the other hand, are a two-way operation where you use the same key for both encryption and decryption of your message (see Figure 6-1). You can reverse the encryption process to recover the original data (provided that you use the same key), which is why it is referred to as symmetric.
../images/457525_1_En_6_Chapter/457525_1_En_6_Fig1_HTML.png
Figure 6-1

Symmetric encryption uses the same key for encryption and decryption

Symmetric encryption has both advantages and disadvantages to its use.

Advantage: Very Secure

When using a secure algorithm such as AES, symmetric encryption is exceptionally secure. One of the most widely used symmetric key encryption systems is the U.S. government-designated Advanced Encryption Standard. As of the writing of this book, AES is unbroken, so it is one of the recommended algorithms.

Advantage: Fast

One of the current problems with public key encryption systems (such as RSA which we explore in Chapter 7) is that they need complicated mathematics to work, making them very computationally intensive and slow. Encrypting and decrypting symmetric key data is easier to do, giving you excellent read and write performance. In fact, many solid-state drives, which are very fast, use symmetric key encryption store data, yet they are still a lot faster than unencrypted standard hard drives.

Disadvantage: Sharing Keys Is Hard

One of the most significant problems with symmetric key encryption algorithms is that you need to have a way to get the key to the person with who you are sending the encrypted data. Encryption keys aren’t simple strings of text like passwords; they are byte arrays of randomly generated data, such as the random numbers we generated with RNGCryptoServiceProvider earlier in this book. As such, you’ll need to have a safe way to get the key to the other person.

Symmetric key encryption is particularly useful when encrypting your information as opposed to when sharing encrypted information. There are ways to use the power of symmetric encryption with a suitable key sharing scheme, which we look at later in the book when I talk about hybrid encryption schemes.

Disadvantage: Dangerous If Compromised

When someone gets hold of one of your symmetric keys, they can decrypt everything encrypted with that key. When you’re using symmetric encryption for two-way communications, this means that both sides of the conversation get compromised. With asymmetrical public-key cryptography like RSA, someone that gets your private key can decrypt messages sent to you but can’t decrypt what you send to the other party since that is encrypted with a different key pair.

History of DES and Triple DES

The Data Encryption Standard, or DES, was created in the early 1970s at IBM, and later, in 1977, the algorithm was submitted to the National Bureau of Standards (NBS) to be approved as Federal Information Processing Standard 46 (FIPS 46).

With consultation from the National Security Agency (NSA), the National Bureau of Standards accepted a slightly modified version of DES as the standard FIPS 46 in the same year to provide security for the unclassified electronic data of the U.S. government.

The data is encrypted in DES using 64-bit blocks, where the input data to be encrypted is split into 64-bit (8-byte) chunks, which are encrypted using a 56-bit symmetric key to provide confidentiality and privacy (see Figure 6-2). Although DES is not the recommended standard for encryption these days, there are still a lot of legacy systems that use DES, especially in mainframe banking applications; this makes looking at DES still relevant for discussion in this course, because you may have to maintain or integrate with older systems that encrypt their data with DES.
../images/457525_1_En_6_Chapter/457525_1_En_6_Fig2_HTML.png
Figure 6-2

DES uses a short 56-bit key

The original DES cipher’s key size of 56 bits was sufficient when the algorithm was designed, but the availability of increasing computational power made brute-force attacks more feasible.

There were several projects to try and break DES. The most well-known is the DESCHALL Project. If you want to read more about how it was done, and it is a fascinating story, then I recommend you read the book Brute Force: Cracking the Data Encryption Standard by Matt Curtin (Copernicus Books, 2005).

Once DES was compromised, a new variant was put into action called Triple DES. Triple DES represented an easy way of increasing the key size of DES to protect against such attacks, without the need to design an entirely new block cipher algorithm. Many former DES users now use Triple DES. Triple DES involves applying DES three times with two or three different keys. Triple DES is regarded as adequately secure, although it is quite slow. In Figure 6-3, you can see that three instances of DES are set to run in series.
../images/457525_1_En_6_Chapter/457525_1_En_6_Fig3_HTML.png
Figure 6-3

The three-key variant of Triple DES

The plaintext is first encrypted with Key 1.

The output of this encryption is then encrypted with Key 2.

Then the result of the data encrypted with Key 2 is then encrypted with Key 3.

This then gives you your final encrypted ciphertext.

To go from ciphertext back to the original plaintext you run the process in reverse. Another variation of Triple DES is the two-key version shown in Figure 6-4.
../images/457525_1_En_6_Chapter/457525_1_En_6_Fig4_HTML.png
Figure 6-4

The two-key variant of Triple DES

In this version, the plaintext is encrypted with Key 1.

The output of this encryption is then re-encrypted using Key 2.

Then the output of this is re-encrypted with Key 1.

The three-key variant of DES offers the best security for this algorithm with a total key length of 168 bits. The two-key variant offers a compromise on key length (112 bits) between the original DES and the three-key variant.

How DES and Triple DES Works

The internal structure of DES is called a block cipher, an algorithm that takes a fixed-length stream of plaintext bytes and processes them through a series of operations into a ciphertext byte stream of the same length.

The diagram in Figure 6-5 shows that the block size is 64 bits with DES. DES also uses a key to customize the encryption process, so that decryption can only be achieved by the person in possession of the same key that was used to encrypt. This is why this type of encryption is referred to as symmetric encryption.

The key is represented as 64 bits (8 bytes); however, only 56 of these are used by the algorithm. Eight bits are solely for error and parity checking which are then discarded; this means the effective key length is 56 bits.

As with other block-based encryption algorithms, DES by itself is not a secure system of encryption. DES must instead be used in different modes, which I discuss later in this chapter.
../images/457525_1_En_6_Chapter/457525_1_En_6_Fig5_HTML.png
Figure 6-5

The internal structure of DES

The algorithm’s overall structure is shown in the preceding diagram. There are 16 identical stages of processing which are called rounds. At the start of the process, there is an initial permutation. At the end of the encryption process, there is a final permutation. The initial permutation and final permutation have no cryptographic significance. They were included to make loading blocks in and out of mid-1970s 8-bit-based hardware more straightforward.

Before the main rounds, the block is divided into two 32-bit halves and processed alternately. This crisscrossing is known as the Feistel scheme, which is named after the German-born physicist and cryptographer Horst Feistel who performed pioneering research while working for IBM.

The Feistel structure is beneficial as the encryption, and decryption processes are similar which means they need only the key schedule to be reversed. This means that the size of the software required to implement such a cipher is nearly halved. The circle symbol with a cross in it, on the diagram, denotes the exclusive-OR (XOR) operation. The F-functions in the boxes scramble half of a block together with part of the encryption key.

The output from that function is then combined with the other half of the block. Then the halves are swapped before the next round in the process. After the final round is computed, the remaining halves are then swapped again; this is the feature of the Feistel structure that makes the encryption and decryption processes similar. DES uses a key schedule for encryption, which is an algorithm that generates the subkeys used for each of the 16 rounds.

First, the 56-bit key is divided into two 28-bit halves. Each half is then treated separately. In successive rounds, both halves are rotated left by one or two bits, and then 48 subkey bits are selected—24 bits from the left half and 24 from the right. These rotations mean that a different set of bits is used in each subkey. The key schedule for decryption is similar, and the subkeys are in reverse order compared to encryption. Apart from that change, the process is the same as for encryption.

The encryption processes just described may seem complicated, but luckily, you do not need to know all of this in detail to use DES because the .NET implementation abstracts it away from you; but it is good to have an idea about how these algorithms work.

History of AES

The Advanced Encryption Standard (AES) is the latest encryption standard adopted by the National Institute of Standards and Technology (NIST) in 2001 for the symmetric encryption of messages. The AES algorithm was selected as part of a contest to find a replacement for DES. The AES algorithm is based on the Rijndael cipher that was developed by two Belgian cryptographers, Joan Daemen and Vincent Rijmen. Joan and Vincent submitted a proposal to NIST during the AES competition selection process. Rijndael is a family of ciphers that use different key and block sizes. For AES, NIST selected three different members of the Rijndael family. Each of these members has a block size of 128 bits, but three different key lengths: 128, 192, and 256 bits (see Figure 6-6).
../images/457525_1_En_6_Chapter/457525_1_En_6_Fig6_HTML.png
Figure 6-6

AES uses three different key sizes

AES is now used worldwide; it supersedes the DES. Like the DES algorithm, the AES algorithm is symmetric, which means that the same key is used for both encrypting and decrypting the data.

NIST announced that AES is the US standard FIPS 197 on November 26, 2001. This announcement followed a five-year standardization process in which 15 competing designs were presented and evaluated; the Rijndael cipher was selected as the most suitable. AES became active as a US government standard on May 26, 2002, after approval by the US Secretary of Commerce.

How AES Works

Unlike DES, AES does not use a Feistel network. AES is a variant of the Rijndael cipher that uses a fixed-block size of 128 bits and a variable sized key of 128, 192, or 256 bits. AES is based on a design principle that is known as a substitution-permutation network; this is a combination of both substitution and permutation and is fast in both software and hardware (see Figure 6-7).
../images/457525_1_En_6_Chapter/457525_1_En_6_Fig7_HTML.png
Figure 6-7

The internal structure of AES

A substitution box, or S-box , is a fundamental component of a symmetric key algorithm which performs substitutions. In a block cipher, the blocks obscure the relationship between the key and the resulting ciphertext. An S-box is implemented as a fixed lookup table.

A permutation box, or P-box , is a method of bit-shuffling to transpose bits across an S-boxes input. In block ciphers, the S-boxes and P-boxes make the relationship between plaintext and ciphertext challenging to understand.

One of the principal design goals of AES was to keep it more straightforward to implement in both hardware and software. AES works by repeating the same defined steps multiple times, which are called rounds . Each round of encryption comprises of several processing steps, including one that uses an encryption/decryption subkey that is generated from the shared key.

As mentioned, the proper key lengths in AES are 128, 192, and 256 bits. Every key is expanded so that a separate subkey could be utilized for every round. The number of AES rounds generally depends on the length of the key.

How Secure Is AES Against Brute-Force Attacks ?

Current governments and enterprises place a lot of trust in the belief that AES is so secure that its security key can never be broken. The length of the key used in encryption determines the feasibility of performing a brute-force-based attack against it; longer keys are exponentially harder to crack than shorter keys.

A brute-force attack involves testing all possible key combinations of an encryption key until the correct key is discovered. Table 6-1 shows the potential number of key combinations for a given key size.
Table 6-1

Total Combinations for Different Key Sizes

Key Size

Possible Combinations

1-bit

2

2-bit

4

4-bit

16

8-bit

256

16-bit

65536

32-bit

4.2 × 109

56-bit (DES)

7.2 × 1016

64-bit

1.8 × 1019

128-bit (AES)

3.4 × 1038

192-bit (AES)

6.2 × 1057

256-bit (AES)

1.1 × 1077

There is an exponential increase in combinations as the key size increases. From this table, you could argue that a 128-bit symmetric key is computationally secure against a brute-force attack.

Consider the times to crack in Table 6-1 compared to key sizes shown in Table 6-2.
Table 6-2

Time to Brute Force Different Key Sizes

Key Size

Time to Crack

56-bit

399 seconds

128-bit

1.02 × 1018 years

192-bit

1.872 × 1037 years

256-bit

3.31 × 1056 years

Even with a supercomputer at today’s standards, it would take over 100 billion years to compromise a 128-bit AES key using a brute-force attack; this is much longer than the age of the universe, which is roughly 13.75 billion years old. If you assumed that a computational system existed somewhere that could brute force a DES key in about a second, it would still take that same machine roughly 149 trillion years to crack a 128-bit AES key.

Of course, we don’t know what the future of computing holds in store, but for the moment, AES is a very secure algorithm that hasn’t been compromised. Even though a 128-bit key should be more than sufficient, it is common to use a 256-bit key in practice.

This is what our code demo for AES will use.

API Commonality in the .NET Framework

We have looked at the history of each of the encryption algorithms, and we looked beneath the surface at how they work. Now, let’s look at what the .NET Framework has to offer.

The AES, DES, and Triple DES algorithms in .NET all share a common abstract base class, SymmetricAlgorithm, which gives them a lot of standard functionality. Before we look at the specific algorithms and examples of their use, let’s look at some of the characteristic properties associated with them.

Encryption Mode

The first property that we look at is the Mode property on the SymmetricAlgorithm object.

Block cipher algorithms such as AES, DES, and Triple DES encrypt data in block units rather than in single bytes at a time. Block ciphers use the same algorithm toward each block that is processed. Because of this, a block of plaintext always returns the same ciphertext when encrypted with the same key and algorithm.

Let’s discuss the encryption modes.
  • Cipher block chaining (CBC) mode introduces feedback into the encryption process. Before each plaintext block is encrypted, it is combined with the ciphertext of the previous block with a bitwise exclusive OR operation. This ensures that, even if the plaintext contains many identical blocks, they each encrypt to a different ciphertext block. The initialization vector (IV) is combined with the first plaintext block by a bitwise exclusive OR operation before the block is encrypted. We talk about the initialization vector in a moment.

  • Ciphertext feedback (CFB) is an encryption mode that in contrast to the cipher block chaining, encrypts a specific number of bits at a time; it is at times desirable to encrypt and transfer some plaintext values instantly, one at a time. Like cipher block chaining, cipher feedback makes use of an initialization vector (IV). In cipher feedback mode, the previous ciphertext block is encrypted, and the output is XORed with the current plaintext block to create the current ciphertext block. The XOR operation conceals plaintext patterns. Plaintext cannot be directly worked on unless there is a retrieval of blocks from either the beginning or end of the ciphertext.

  • Ciphertext stealing (CTS) handles any length of plaintext and produce ciphertext whose length is the same as the original plaintext length. This mode behaves like the CBC mode for all but the last two blocks of the plaintext.

  • Electronic codebook (ECB) encrypts each block individually. Any blocks that are the same in any given message are translated into identical ciphertext blocks. The disadvantage of the electronic codebook mode is that identical plaintext blocks are encrypted to identical blocks and do not hide patterns of data very well. In some ways, it doesn’t provide message confidentiality at all, and so it is not recommended for modern cryptographic protocols.

  • Output feedback (OFB) processes small increments of plaintext data into ciphertext instead of treating an entire block at a time. This mode is similar to cipher feedback except it differs in the way the shift register is used. If a bit in your ciphertext changes, the corresponding bit of plaintext is replaced. However, if there are missing or extra bits of data from the ciphertext, the plaintext is mangled from that point on. The default mode in .NET for AES, DES, and Triple DES is cipher block chaining, and unless you have a reasonable need to change that default mode, you should go with the default.

Padding

The next property in SymmetricAlgorithm is padding, which specifies the padding to apply when the message block being encrypted is shorter than the total number of bytes needed for an encryption operation. There are different padding schemes, such as
  • ANSI X923

  • ISO 10126

  • None

  • PKCS7

  • Zeros

The default padding in .NET for AES, DES, and Triple DES is the PKCS7 mode, and unless you have a good reason to change it, you should use the default.

Key

The Key property is a byte array that stores the encryption key prior to running encrypt and decrypt operations. The data in this property can literally be anything, but you should make sure you generate a secure key. There are two ways to do this. You can use the RNGCryptoServiceProvider object and generate a byte array to the desired key length, or you can call the GenerateKey() method. Under the covers the GenerateKey() method uses RNGCryptoServiceProvider to generate its key. Either way is fine. The examples used in this chapter all use the RNGCryptoServiceProvider object to generate the random number.

Initialization Vector (IV)

The initialization vector (IV) property is a byte array that stores a random number that is used along with the key for encryption. This number is also called a nonce (number once) and is employed only once in any encryption session. The use of an IV prevents repetition in data encryption, making it more difficult for a hacker who is using a dictionary attack to find patterns to break a cipher. For example, a sequence might appear twice or more within the body of a message.

If there are repeated sequences in encrypted data, an attacker could assume that the corresponding sequences in the plaintext message were the same. The initialization vector prevents the appearance of duplicated data sequences in the resulting ciphertext. Ideally, the initialization vector is a random number that is only known by the destination computer performing the decryption operation. The initialization vector can be agreed in advance of the encryption operation, transmitted independently, or included as part of the session that is set up before the exchange of the message data.

The length of the initialization vector (the number of bits or bytes it contains) depends on the method of encryption. The initialization vector does not have to be kept secret and can be transmitted or stored along with the message in the clear.

AesManaged and AesCryptoServiceProvider

The .NET Framework and .NET Core provide two implementations of the AES encryption algorithm, AesManaged, and AesCryptoServiceProvider . Which one should you use?

They both provide the same functionality in that they both implement the AES encryption specification, but the main difference is that AesManaged is a .NET-specific implementation, whereas AesCryptoServiceProvider uses the underlying cryptography libraries in Windows, macOS, or Linux, which are FIPS certified.

The National Institute of Standards and Technology (NIST) issued the FIPS 140-2 certification for cryptography modules that include both hardware and software. If you want to ensure you are encrypting data with AES by using a compliant implementation, then AesCryptoServiceProvider is the implementation that you want to use, especially if you need to interoperate with systems that are also compliant with FIPS 140-2. The code examples in the demo are based around AesCryptoServiceProvider and not AesManaged, although their usage is very similar.

Performing Symmetric Encryption with .NET

Now that we have spent time on symmetric encryption in .NET with DES, Triple DES, and AES, let’s look at some example implementations. It is not a good idea to use DES or Triple DES in new software systems. If you are developing a new application, then you should use AES. If you work in an enterprise in which you have to integrate with older legacy systems that require you to encrypt or decrypt with DES or Triple DES, then understanding how to use them in .NET is a benefit for you. In the following code examples, I show you how to configure both the two- and three-key variations of Triple DES.

Before we look at a DES implementation, we first need to revisit our old friend the random-number generator quickly. The following code should look familiar to you because we used it in previous chapters. Here we need it to generate our symmetric encryption keys and initialization vectors.
public byte[] GenerateRandomNumber(int length)
{
    using (var randomNumberGenerator = new RNGCryptoServiceProvider())
    {
        var randomNumber = new byte[length];
        randomNumberGenerator.GetBytes(randomNumber);
        return randomNumber;
    }
}

In the GenerateRandomNumber method, we pass in an integer that represents the length of the byte array that we want to fill with random data. The random numbers are generated using the RNGCryptoServiceProvider object in the .NET Framework.

In the following code, a method encrypts data using DES.
public byte[] Encrypt(byte[] dataToEncrypt,
                      byte[] key,
                      byte[] iv)
{
    using (var des = new DESCryptoServiceProvider())
    {
        des.Mode = CipherMode.CBC;
        des.Padding = PaddingMode.PKCS7;
        des.Key = key;
        des.IV = iv;
        using (var memoryStream = new MemoryStream())
        {
            var cryptoStream =
                 new CryptoStream(memoryStream,
                 des.CreateEncryptor(),
                 CryptoStreamMode.Write);
            cryptoStream.Write(dataToEncrypt, 0,
                               dataToEncrypt.Length);
            cryptoStream.FlushFinalBlock();
            return memoryStream.ToArray();
        }
    }
}

The Encrypt method takes three parameters, each of them byte arrays. The first parameter is the data that we want to be encrypted. The next parameter is the encryption key, and the final parameter is the initialization vector. The first thing that the Encrypt method does is to create an instance of the DESCryptoServiceProvider object. Then the padding and encryption modes are set explicitly. The values set in this example are the default values, but I have set them to be explicit. Next, the key and the initialization vectors are assigned that were passed in as parameters. I always like to generate them myself and pass them in, but on the DESCryptoServiceProvider object, there are also GenerateKey and GenerateIV methods that you can use instead if you so prefer.

Next, instances of MemoryStream and CryptoStream are created. The symmetric encryption libraries in .NET are stream based, and the cryptography operations happen within CryptoStream. As we are passing in the data to be encrypted into this method, we can use a memory stream, but you could also use FileStream.

When we create the instance of the CryptoStream object , we pass in des.CreateEncryptor(), which sets up the crypto stream with our configured DESCryptoServiceProvider object.
var cryptoStream =
      new CryptoStream(memoryStream,
      des.CreateEncryptor(),
      CryptoStreamMode.Write);
Once the stream objects are set up, we then need to perform the actual encryption operation using the following code.
cryptoStream.Write(dataToEncrypt, 0, dataToEncrypt.Length);
cryptoStream.FlushFinalBlock();
return memoryStream.ToArray();

The call to Write takes the data we want to encrypt and its length in bytes and performs the encryption operation. Then we call FlushFinalBlock , which updates the data source with the current state of the buffer. Then the buffer is cleared. Our encrypted data is returned to the calling method.

Now that we have encrypted data, how do we decrypt it? The code for decrypting our data is almost identical to the method we just explored.
public byte[] Decrypt(byte[] dataToDecrypt,
                      byte[] key,
                      byte[] iv)
{
    using (var des = new DESCryptoServiceProvider())
    {
        des.Mode = CipherMode.CBC;
        des.Padding = PaddingMode.PKCS7;
        des.Key = key;
        des.IV = iv;
        using (var memoryStream = new MemoryStream())
        {
            var cryptoStream =
                 new CryptoStream(memoryStream,
                 des.CreateDecryptor(),
                 CryptoStreamMode.Write);
            cryptoStream.Write(dataToDecrypt, 0,
                               dataToDecrypt.Length);
            cryptoStream.FlushFinalBlock();
            return memoryStream.ToArray();
        }
    }
}

At first glance, this code looks almost identical. Of course, the method name is different because we called Decrypt this time, and instead of passing in data to encrypt, we are passing in our encrypted data. The key and initialization vector should be the same as those used for the encryption process. If they are not the same, we will not be able to decrypt our data.

Apart from the method name and parameters, the only other difference in this method is the following lines of code.
var cryptoStream =
       new CryptoStream(memoryStream,
       des.CreateDecryptor(),
       CryptoStreamMode.Write);
       cryptoStream.Write(dataToDecrypt, 0,
             dataToDecrypt.Length);

When we create the CryptoStream, instead of passing in the CreateEncryptor method on the DESCryptoServiceProvider instance, we pass in CreateDecryptor instead, also on the call to Write on the CryptoStream, we pass in the encrypted data that we want to be decrypted. These are the only differences between the code for encrypting and decrypting with DES. The code for performing Triple DES and AES is also very similar.

Let’s now look at a simple console application that calls into this encryption and decryption for DES.
class Program
{
    static void Main(string[] args)
    {
        var des = new DesEncryption();
        var key = des.GenerateRandomNumber(8);
        var iv = des.GenerateRandomNumber(8);
        const string original = "Text to encrypt";
        var encrypted = des.Encrypt(
                      Encoding.UTF8.GetBytes(original),
                      key, iv);
        var decrypted = des.Decrypt(encrypted, key, iv);
        var decryptedMessage =
                  Encoding.UTF8.GetString(decrypted);
        Console.WriteLine("DES Demonstration in .NET");
        Console.WriteLine("-------------------------");
        Console.WriteLine();
        Console.WriteLine("Original Text = " + original);
        Console.WriteLine("Encrypted Text = "
                  + Convert.ToBase64String(encrypted));
        Console.WriteLine("Decrypted Text = "
                  + decryptedMessage);
        Console.ReadLine();
    }
}

The demo application starts by creating an instance of a class containing our encryption and decryption methods. Then we create the key and initialization vector. The key we generate is 8 bytes (64 bits). The actual DES key that is used is 56 bits, so our key is padded for the extra bits. The initialization vector is also 8 bits in length.

Next, the code performs our actual encryption and decryption operations.
var encrypted = des.Encrypt(
          Encoding.UTF8.GetBytes(original),
          key, iv);
var decrypted = des.Decrypt(encrypted, key, iv);
var decryptedMessage =
          Encoding.UTF8.GetString(decrypted);

When we call the Encrypt operation, we first have to convert the string we want to encrypt into a byte array. In this case, we are using UTF8.GetBytes to perform this conversion. When the Encrypt method returns, it passes back a byte array containing the encrypted data, which means that when we call decrypt, we do not have to perform any conversions because the data to decrypt is already a byte array.

When the Decrypt operation returns it passes back the unencrypted data, but it is also in byte array form, so if we want to display it to the user in its original form, we first have to convert it to a string using UTF8.GetString. When we run the demo, we end up with something like you see in Figure 6-8.
../images/457525_1_En_6_Chapter/457525_1_En_6_Fig8_HTML.jpg
Figure 6-8

The result of DES encryption

Now that we have looked at how to use DES in .NET, let’s expand on it to use Triple DES. The only changes to our original encrypt and decrypt methods are the name of the CryptoServiceProvider class that we instantiate. In the demo we just looked at, it was DESCryptoServiceProvider, but in the next demo, it is TripleDESCryptoServiceProvider . This means our Encrypt method looks like the following.
public byte[] Encrypt(byte[] dataToEncrypt,
                      byte[] key, byte[] iv)
{
    using (var des = new TripleDESCryptoServiceProvider())
    {
        des.Mode = CipherMode.CBC;
        des.Padding = PaddingMode.PKCS7;
        des.Key = key;
        des.IV = iv;
        using (var memoryStream = new MemoryStream())
        {
            var cryptoStream =
                new CryptoStream(memoryStream,
                des.CreateEncryptor(),
                CryptoStreamMode.Write);
            cryptoStream.Write(dataToEncrypt, 0,
                dataToEncrypt.Length);
            cryptoStream.FlushFinalBlock();
            return memoryStream.ToArray();
        }
    }
}

As you can see, the only work that has changed in the Encrypt method is the class name TripleDESCryptoServiceProvider. The same is also true for the Decrypt method. The only change is the change from DESCryptoServiceProvider to TripleDESCryptoServiceProvider , so there is no need to repeat it here.

What is different though is how we handle the keys that we pass into Encrypt and Decrypt. We discussed earlier that you could use Triple DES in two configurations. The first uses two encryption keys and the second uses three encryption keys.

The following is an example sample application that uses our encryption code.
class Program
{
    static void Main(string[] args)
    {
        var tripleDes = new TripleDesEncryption();
        var key = tripleDes.GenerateRandomNumber(16);
        var iv = tripleDes.GenerateRandomNumber(8);
        const string original = "Text to encrypt";
        var encrypted = tripleDes.Encrypt(
                   Encoding.UTF8.GetBytes(original), key, iv);
        var decrypted = tripleDes.Decrypt(encrypted, key, iv);
        var decryptedMessage =
                  Encoding.UTF8.GetString(decrypted);
        Console.WriteLine("Triple DES Demonstration in .NET");
        Console.WriteLine("--------------------------------");
        Console.WriteLine();
        Console.WriteLine("Original Text = " + original);
        Console.WriteLine("Encrypted Text = " +
                           Convert.ToBase64String(encrypted));
        Console.WriteLine("Decrypted Text = " + decryptedMessage);
    }
}
This code is very similar to the code we used to test DES, but in this example, there is one main difference in the size of the encryption key that we are generating.
var key = tripleDes.GenerateRandomNumber(16);
Instead of generating a single 8-byte key, of with 56 bits are used, and the rest is padding, we are creating 16 bytes; this means the .NET of Triple DES operate in the two-key variation. If we want to use the three-key variation, then we generate a key that is 24 bytes long by calling the following code.
var key = tripleDes.GenerateRandomNumber(24);
Now that we have explored DES and Triple DES in some detail, what about the AES?
public byte[] Encrypt(byte[] dataToEncrypt,
                      byte[] key,
                      byte[] iv)
{
    using (var aes = new AesCryptoServiceProvider())
    {
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
        aes.Key = key;
        aes.IV = iv;
        using (var memoryStream = new MemoryStream())
        {
            var cryptoStream = new CryptoStream(
                memoryStream,
                aes.CreateEncryptor(),
                CryptoStreamMode.Write);
            cryptoStream.Write(dataToEncrypt, 0,
                               dataToEncrypt.Length);
            cryptoStream.FlushFinalBlock();
            return memoryStream.ToArray();
        }
    }
}
Luckily, the code to use AES in .NET is almost identical to what we just looked at for DES and Triple DES. As you can see in the preceding example code, the only change to the code is the service provider class that we instantiate; this time it is AesCryptoServiceProvider , everything else is the same.
static void Main(string[] args)
{
    var aes = new AesEncryption();
    var key = aes.GenerateRandomNumber(32);
    var iv = aes.GenerateRandomNumber(16);
    const string original = "Text to encrypt";
    var encrypted = aes.Encrypt(
                   Encoding.UTF8.GetBytes(original), key, iv);
    var decrypted = aes.Decrypt(encrypted, key, iv);
    var decryptedMessage = Encoding.UTF8.GetString(decrypted);
    Console.WriteLine("AES Encryption in .NET");
    Console.WriteLine("----------------------");
    Console.WriteLine();
    Console.WriteLine("Original Text = " + original);
    Console.WriteLine("Encrypted Text = " +
                      Convert.ToBase64String(encrypted));
    Console.WriteLine("Decrypted Text = " + decryptedMessage);
    Console.ReadLine();
}
With the AES code almost identical to the DES and Triple DES, the only difference in the usage of the AES encryption and decryption code are the key sizes and the size of the initialization vector. The initialization vector is 16 bytes. For the key sizes, we have three different options: 128 bits, 192 bits, and 256 bits. This means the byte arrays are 16 bytes, 24 bytes, and 32 bytes, respectively. The preceding code sample uses 32 bytes or 256 bits for the key that has the most robust size. I would say that unless you have a specific reason to use a small key size, go straight for the 256-bit (32 bytes) key. The result of our AES sample application is seen in Figure 6-9.
../images/457525_1_En_6_Chapter/457525_1_En_6_Fig9_HTML.jpg
Figure 6-9

The result of AES encryption

Summary

In this chapter, we started exploring the confidentiality pillar in our pursuit of using cryptography. Our primary focus was to look at symmetric encryption. Symmetric encryption is where you use the same key to both encrypt and decrypt data. Symmetric encryption is fast and efficient due to its algorithmic nature, but it does have one major downside in that sharing the encryption key between parties is hard to do securely, which is something we explore later in the book.

We started by looking at DES and its more secure variant, Triple DES. Although DES and Triple DES are not recommended in new software solutions, they are worth talking about because there are many legacy systems that use a variant of DES internally, so you may be in a situation where you need to encrypt or decrypt data with DES.

AES is preferable encryption algorithm to use in .NET. AES is a modern and secure encryption algorithm that allows you to use key sizes of 128 bits, 193 bits, or 256 bits.

All three encryption algorithms inherit from the SymmetricAlgorithm base class, which means they are all identical to use which is excellent for programmer usability. The main differences are in the key sizes utilized and the sizes of the initialization vectors.

In the next chapter, we continue our exploration of the confidentiality pillar by looking at asymmetric cryptography.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.7.7