Use a cryptographic random number generator in C#


random number

The Random class generates random numbers, but they aren’t cryptographically secure.

What does “cryptographically secure” mean? It means that an attacker, after seeing a series of random numbers that you generate, cannot predict the next one with any success.

This example uses a RNGCryptoServiceProvider to generate random numbers. (RNG stands for Random Number Generator.) The following code shows the RandomInteger method that generates random integers between an upper and lower bound.

// The random number provider.
private RNGCryptoServiceProvider Rand =
    new RNGCryptoServiceProvider();

// Return a random integer between a min and max value.
private int RandomInteger(int min, int max)
{
    uint scale = uint.MaxValue;
    while (scale == uint.MaxValue)
    {
        // Get four random bytes.
        byte[] four_bytes = new byte[4];
        Rand.GetBytes(four_bytes);

        // Convert that into an uint.
        scale = BitConverter.ToUInt32(four_bytes, 0);
    }

    // Add min to the scaled difference between max and min.
    return (int)(min + (max - min) *
        (scale / (double)uint.MaxValue));
}

At the class level, the program creates the RNGCryptoServiceProvider. The RandomInteger method uses that object to generate random numbers.

All the RNGCryptoServiceProvider does is generate bytes. It’s up to you to convert those bytes into whatever values you need.

The RandomInteger method starts by setting the value scale to uint.MaxValue. I don’t want the method to use that value for scale (you’ll see why in a moment) so the method enters a while loop that executes as long as scale is uint.MaxValue.

Inside the loop, the method uses the RNGCryptoServiceProvider to generate four bytes. It then uses BitConverter.ToUInt32 to convert those four bytes into a four-byte unsigned integer (int) and sets scale equal to that value. If the bytes span all of the possible byte values (and they should), then the unsigned integer spans all possible unsigned integer values.

If scale is still uint.MaxValue, then the loop repeats until it gets a new value. (It is extremely unlikely that scale will be uint.MaxValue and much less likely that this will happen twice in a row so the loop won’t last long.)

The code then divides scale by the uint.MaxValue. This produces a value between 0.0 and 1.0, not including 1.0. (This is why I didn’t want scale to be uint.MaxValue–so the result of this division would be less than 1.0.) It multiplies this value by the difference between the maximum and minimum desired values and adds the result to the minimum value.

The final result is a value between min and max, not including max. The code then uses the (int) cast operator to truncate the result to get an integer. This matches the behavior provided by the Random class’s Next method. It returns an integer between a lower bound (inclusive) and an upper bound (exclusive).

When you fill in the values and click Generate, the program generates random numbers and displays histograms showing their distribution. The more numbers you generate, the closer the histograms’ bars should be to the same height.

First the program uses the Random class to generate random numbers and displays their histogram in the top PictureBox. Then it uses the RandomInteger method to generate a new set of numbers and displays their histogram in the bottom PictureBox. Looking at the histograms, both seem pretty “random.”

Using a cryptographic random number generators has advantages and disadvantages.

  • + It generates a different sequence even if you initialize it twice in rapid succession. If you do this with two Random objects, they are both initialized to the current time. If you do it quickly enough, they’ll generate the same sequence of numbers. In fact, if you do this to quickly generate several random numbers, they may all be the same. (You can avoid this by only creating 1 Random object and reusing it each time you need a new number.)
  • + An attacker cannot guess your random numbers. (Note that this isn’t necessary for most programs.)
  • – It’s slow. In one test generating 10 million numbers, the Random class finished in 0.25 seconds but the RNGCryptoServiceProvider took 3.68 seconds, almost 15 times as long.
  • – It’s not repeatable. Sometimes it’s useful to generate the same “random” sequence of numbers repeatedly so you can test a program. You can’t do that with a cryptographic random number generator.

Download the example to see additional details. Later posts will use this method to easily generate cryptographically secure random numbers.


Download Example   Follow me on Twitter   RSS feed




This entry was posted in algorithms, cryptography, mathematics and tagged , , , , , , , , , , . Bookmark the permalink.

5 Responses to Use a cryptographic random number generator in C#

  1. Pingback: Generate random passwords in C#

  2. Melle Koning says:

    Hi Rod,

    Thanks, I needed an alternative to the default Random generator and this explanation is very useful; thanks also for the good explanation of the algorithm. It’s indeed a bit slower but it seems that automated scanners of source code, like VeraCode, prefer towards these Crypto Random generators and give warnings about the source code when we use the default Random number generator.

  3. Eric Zalas says:

    Hi Rod,
    I’m a euchre expert finishing my fourth book on the mathematical aspects of the game. I recently wrote to the creator of an online euchre game (Trickster) and told him that I thought the distribution of hands was not random, presenting statistical data to support my contention. He responded by saying that the game uses the .NET cryptographic random number generator to deal the cards randomly. I did some research on Microsoft .NET System Random class and several articles said that is uses a predetermined table to cycle a list of random numbers and thus, not really random. I’m a retired MBA with a good statistics background; not a programmer. Any thoughts if the online euchre game hands being dealt are not really random? Thanks, Eric

    • RodStephens says:

      Well, more or less nothing on a computer is truly random. You need something like a FM radio receiver monitoring random background noise or a source of radioactive decay to get true randomness.

      But even a mediocre random number generator is probably good enough for a card game. You don’t need true randomness, just enough that the hands are unpredictable. For example, you’re not going to memorize 10,000 hands so you see one and say, “Ah ha! I know what he has!”

      I can’t find the period (how quickly it repeats) for Random and the cryptographic number generator (CRNG) right now, but it’s something like several thousand for Random and a ridiculously large number for the CRNG. Like on the order of the number of atoms in the universe. So repetition isn’t really an issue.

      Probably the real question is, “How good is the distribution of cards?” In other words, it would be “randomish” but bad if the program gave every player random matching cards. I.e. you get a bunch of kings, the next player gets a bunch of aces, and so on. You wouldn’t be able to guess the other players’ cards, but it would still be bad.

      That’s more a function of how the program uses the CRNG to generate the cards. It’s not too hard to get it right, though, so my guess is that there result is pretty good. They may be willing to tell you the exact algorithm they use. It shouldn’t matter that you know.

      Note that a good random sequence does not provide exactly the same frequency of every card all the time. For example, there will be very rare hands where someone is dealt all aces. To really know if you have a “good” random distribution, you would need to look at a huge number of hands. Even then, the result shouldn’t be perfectly smooth.

      Take a look at this example: Use a cryptographic random number generator in C#. You’ll see from the graph that the result isn’t perfectly smooth no matter how many samples you take. They just get smoother as a percentage. If you run the program with 1 million or 10 million samples, it starts to look pretty smooth as a percentage.

      One thing you can note, however, is if the program is making a big mistake. For example, if no hand ever initially contains a king. That seems pretty unlikely, though.

      I hope that helps.

      (By the way, the algorithms used for CRNGs and other cryptography are pretty interesting. If you’re curious and like mathematics, I recommend Bruce Schneier’s book Applied Cryptography, 2nd edition.)

Leave a Reply

Your email address will not be published. Required fields are marked *