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
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 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).
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.
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.
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
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
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.