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

3. The Importance of Random Numbers

Stephen Haunts1 
(1)
Belper, Derbyshire, UK
 

The encryption algorithms discussed in Chapter 2 require a source of random data to generate new symmetric keys. Most computers do not have a hardware-based random number generator, so software developers need to use a software-based implementation to generate random numbers that are suitable.

Because random numbers are generated in software, they are rarely completely random; they are typically pseudorandom ; that is, they appear random, but are not random. To create random data, you need a source of entropy or random input.

Modern cryptographic algorithms are built around Kerckhoff’s principle, which states that the “security of the system must depend solely on the key material, and not on the design of the system.” This means that the strength of modern cryptographic algorithms is determined by the number of bits an attacker needs to guess to break the algorithm. In this book, for example, we use 256-bit keys (32 bytes) with AES and 2048- and 4096-bit keys with RSA. The strength also implies that a potential attacker does not know of any of the bits used in the encryption keys. The strength of a key and algorithm starts to diminish when new attacks against the algorithm are found, and any of the keys can be derived by looking at the encrypted output.

Some random-number generators ask you to move your mouse or use your keyboard as a source of that entropy; others take events such as hard-drive activity or network activity for their source of entropy. Entropy is a measure of uncertainty or randomness associated with data. Creating entropy through physical movements typically works well for computer workstations but can be a problem in some cases, such as server hardware with a network card, some Flash RAM, and CPUs. A network appliance that performs encryption has no good sources of entropy, apart from network events, which an attacker could manipulate to their advantage. Before we look at implementation of a reliable random number generator, let’s take a look at the common System.Random class that is in .NET.

Generating Deterministic Random Numbers

Before becoming familiar with cryptography in .NET, you might have used the random number generator in the System namespace. This is fine for simple scenarios like creating lottery numbers or simulating a dice roll, because it gives the appearance of randomness if you provide a different seed value every time. If you provide the same initial seed value, you will get the same numbers out of the random number generator when you run it. This deterministic nature is no good for generating secure cryptographic random numbers.

To use the System.Random number generator, you must first create an instance of the class and provide a seed value. This seed value is a starting value to initialize the random number generator.
using System;
public class Example
{
   public static void Main()
   {
      Random rnd = new Random(1234);
      for (int ctr = 0; ctr < 10; ctr++) {
         Console.Write("{0,3}   ", rnd.Next(-10, 11));
      }
   }
}

If you provide the same seed value into different random-number generator instances, then you will generate the same set of random numbers. If creating a deterministic series of numbers is what you require, then this way of using System.Random is perfectly fine. You may need to generate deterministic test data or example for your unit tests. If you wanted to ensure that you create different random numbers each time, then you should make your seed value time-dependent, which means that you are guaranteed a different seed value every time.

While using a time-variant seed value offers more random and non-deterministic numbers, it is not recommended for generating random numbers suitable for cryptography. Another issue with System.Random is that it is not thread safe; so if you are using System.Random, you need to use thread locking around each call. Let’s not dwell on System.Random any longer, and instead look at a better solution, which is RNGCryptoServiceProvider.

Generating Secure Random Numbers

As you have seen, random numbers are critical in using cryptography efficiently because we need good, non-deterministic random numbers to create symmetric and asymmetric encryption keys.

System.Random is not very good at generating non-deterministic random numbers. To produce cryptographically secure random numbers, the RNGCryptoServiceProvider class uses an internal Windows cryptographic service provider to create the number. RNGCryptoServiceProvider is a much safer way of generating these numbers, and it is the technique that Microsoft recommends. The trade-off for securer random numbers is that RNGCryptoServiceProvider is much slower to execute compared to System.Random, but this is a small trade-off when you want to deal with numbers being generated to use as encryption keys.

RNGCryptoServiceProvider internally uses an implementation called CryptGenRandom and more specifically a function called RtlGenRandom to generate its random numbers. This random number function uses entropy from the following sources:
  • The current running process ID

  • The current thread ID

  • A tick counter from since the time the machine was rebooted

  • The current time

  • High-precision performance counters

  • A hash of user data, such as username, computer name, and so forth

  • Internal high-precision timers

While all of this sounds complicated, the good news is that you don’t need to understand the internal workings of any of the algorithms discussed in the book. You are not here to learn about theoretical cryptography, but to learn how to apply it in your .NET application. With that, let’s look at how you generate a cryptographically secure random number using RNGCryptoServiceProvider .
using System.Security.Cryptography;
public class Random
{
    public static byte[] GenerateRandomNumber(int length)
    {
          using ( var randomNumberGenerator =
                 new RNGCryptoServiceProvider())
        {
            var randomNumber = new byte[length];
            randomNumberGenerator.GetBytes(randomNumber);
            return randomNumber;
         }
    }
}

As you can see, making use of RNGCryptoServiceProvider is quite straightforward. The preceding method, GenerateRandomNumber, takes an integer that represents the length of the random number we want to generate. This length is the number of bytes we want to produce. If you require a 256 bit random number, then you would pass in 32, which is 32 bytes. Next, we create an instance of the RNGCryptoServiceProvider class, and then we create a new byte array that is initialized to the length we passed into the GenerateRandomNumber method. Now we call the GetBytes method on the RNGCryptoServiceProvider class instance and pass in the byte array that we just created. This fills that byte array with random data, which is then returned from the method.

That is all it takes. It is that simple. The following is a small sample program that uses the random number generator to create 20 sets of random numbers.
class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 20; i++)
        {
            string randomNumber =
                Convert.ToBase64String(
                    RandomNumber.GenerateRandomNumber(32));
            Console.WriteLine(randomNumber);
        }
Console.ReadLine();
    }
}
The code iterates 20 times using a for loop and calls our random number generator. The random number generator returns a byte array. To display this random number on a screen (see Figure 3-1), we need to convert it to something that is more readable. To do this, we call Convert.ToBase64String, which converts the byte array into a string that we can display on the screen. There is also a method called Convert.FromBase64String, which allows you to go from a base64 string back to a byte array. This is something we will do many times throughout the book. Now that we have a working sample application, let’s look at the result on the screen.
../images/457525_1_En_3_Chapter/457525_1_En_3_Fig1_HTML.jpg
Figure 3-1

Random numbers generated with RNGCryptoServiceProvider

The random numbers we have generated are all 32 bytes in length, which is 256 bits. This is the size of the random number that we use for symmetric encryption later in the book. At a casual glance, you can see that each of the numbers is entirely different, which is what we want for effective cryptography.

Summary

Good quality, non-deterministic, random numbers are one of the essential primitives we will look at in this book. Without genuinely random numbers, the security of our cryptographic algorithms falls apart and becomes susceptible to attackers. We first looked at the System.Random number generator in .NET, which is fine for generating simple lottery numbers or dice rolls, but it is not suitable for cryptography because it is not random enough. The solution recommended by Microsoft is to use RNGCryptoServiceProvider because it generates higher quality, non-deterministic numbers. In the next chapter, we look at our next cryptographic primitive, hashing, which helps satisfy the first pillar—integrity.

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

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