Home > Articles > Programming > C#

.NET Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Cryptographically Secure Random Numbers

Last updated Mar 14, 2003.

The .NET Framework has two ways of generating random numbers. Most programs will instantiate a System.Random object instance and call one of the member functions to get random numbers. The numbers returned aren't truly random, but rather pseudo random. But they're good enough for most applications that call for randomness.

But the pseudo random nature of the numbers returned by System.Random objects is not good enough for cryptographic purposes. The algorithm used by System.Random to generate random numbers is actually generating a sequence in which the next number generated is dependent on the previous number generated. The algorithm is deterministic and predictable. Somebody who knows how the algorithm works can use that information to trivially break any encryption based on the pseudorandom numbers.

Cryptographic applications require truly random sequences that can't be predicted. In .NET, the System.Security.Cryptography.RandomNumberGenerator abstract class serves as the base class for all cryptographic random number generators. System.Security.Cryptography.RNGCryptoServiceProvider provides an implementation of that abstract class.

Generating secure random values

Unlike the System.Random class, RNGCryptoServiceProvider and other RandomNumberGenerator descendents do not have the convenient Next, NextDouble and similar methods. RandomNumberGenerator only provides two methods: GetBytes and GetNonZeroBytes. It'll take a little more work to get random integers or floating point values.

The first step in generating truly random values is to create an instance of the RNGCryptoServiceProvider class. Then, allocate an array of bytes and pass that array to the GetBytes method. The random number generator will fill the byte array with a sequence of random values. Here's how to do it in C#.

RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
byte[] randomBytes = new byte[1024];
random.GetBytes(randomBytes);
foreach (var b in randomBytes)
{
    Console.Write("{0:X2} ", b);
}

You'll need to add a project reference to System.Security, and include a using System.Security.Cryptography line in your code.

RNGCryptoServiceProvider has four constructors. As far as I've been able to determine, they all do the same thing. Documentation for two of the constructors that accept parameters say that the parameters are ignored. The other one that takes parameters doesn't say what values are valid or what the constructor does with them. My advice would be to always call the default constructor.

If all you need is a way to generate a sequence of random bytes, then the above code will work very well for you. It provides the same functionality as calling the NextBytes method on a System.Random object. Note, however, that RNGCryptoServiceProvider will be somewhat slower than System.Random, but not so slow that it'll be noticeable unless you call it thousands of times per second.

Generating random integers

In general, applications that require the use of truly random values don't typically need random integers or random floating point numbers. They just need random bytes. But if you really want random integers or other multi-byte values, you'll have to construct them yourself from the bytes returned by GetBytes. The code below generates 256 random positive integers from the random bytes returned by GetBytes.

RNGCryptoServiceProvider random = new RNGCryptoServiceProvider("testo");
byte[] randomBytes = new byte[256 * sizeof(int)];
random.GetBytes(randomBytes);
for (int i = 0; i < 256; ++i)
{
    int val = BitConverter.ToInt32(randomBytes, i * 4);
    val &= 0x7fffffff;
    Console.WriteLine(val);
}

BitConverter.ToInt32 gets the next four bytes in the array and returns a 32 bit integer. The next line of code just makes sure that the number is positive. If you don't mind getting negative numbers, then you can skip that. Or, you can get unsigned integers by calling BitConverter.ToUInt32.

Creating a System.Random replacement

You can use the technique above to create a replacement for System.Random that returns more truly random values. The trick is to maintain a buffer of random values that you get from the RNGCryptoServiceProvider, and then generate random values from it. Whenever you exhaust the buffer, make another call to refill it. Once you get that working for returning the next nonnegative integer, the rest falls into place very quickly.

public class JimRandom
{
    private const int BufferSize = 1024;  // must be a multiple of 4
    private byte[] RandomBuffer;
    private int BufferOffset;
    private RNGCryptoServiceProvider rng;
    public JimRandom()
    {
        RandomBuffer = new byte[BufferSize];
        rng = new RNGCryptoServiceProvider();
        BufferOffset = RandomBuffer.Length;
    }
    private void FillBuffer()
    {
        rng.GetBytes(RandomBuffer);
        BufferOffset = 0;
    }
    public int Next()
    {
        if (BufferOffset >= RandomBuffer.Length)
        {
            FillBuffer();
        }
        int val = BitConverter.ToInt32(RandomBuffer, BufferOffset) & 0x7fffffff;
        BufferOffset += sizeof(int);
        return val;
    }
    public int Next(int maxValue)
    {
        return Next() % maxValue;
    }
    public int Next(int minValue, int maxValue)
    {
        if (maxValue < minValue)
        {
            throw new ArgumentOutOfRangeException("maxValue must be greater than or equal to minValue");
        }
        int range = maxValue - minValue;
        return minValue + Next(range);
    }
    public double NextDouble()
    {
        int val = Next();
        return (double)val / int.MaxValue;
    }
    public void GetBytes(byte[] buff)
    {
        rng.GetBytes(buff);
    }
}

My example uses a very small buffer of 1,024 bytes, or 256 integers. A larger buffer requires more memory, but will mean fewer calls to refill it. Fewer refill calls means that the amortized cost per random number generated is less. I suspect that a buffer of one megabyte would provide a very good tradeoff between memory use and speed.

The GetBytes method just calls the RNGCryptoServiceProvider.GetBytes method. Having GetBytes return values from the class buffer would be entirely too much trouble.