Random vs RNGCryptoServiceProvider: Know your RNGs
I have been doing some work with random number generation this week. Specifically, I needed to write a utility that would be used to generate a strong key for a symmetric encryption algorithm. Obviously, there is a wide variety of approaches to this type of task. For my solution, I am using the RNGCryptoServiceProvider to randomize a position in an array of allowed characters. While working on the solution, it made me start to wonder how often people may inappropriately use the Random class for random number generation. I'm sure this topic has already been covered more than a few times, but I am going to throw in my two cents as well.
It is important to understand why Random can cause you grief. The randomization is based on a deterministic mathematical algorithm. If you supply the same seed, it will always generate the same sequence of numbers. In an effort to minimize this risk, it is a common practice to use a date and/or time based value as the seed. When possible, a single instance should be re-used rather than instantiating a new instance each time a random number needs to be generated. This is for two reasons. First, there is an increased risk of generating duplicate numbers when a new instance is used each time. This is most obvious when generating a large quantity of numbers within a brief period of time (less than a second). Second, re-using the instance achieves better performance since there is less overhead involved with a single instance.
Consider the following snippets of code.
// Duplicate Numbers...here we come!
for (int i = 0; i < 100; i++)
{
Random rnd = new Random();
int randomNumber = rnd.Next();
Console.WriteLine("Generated {0} at {1}", randomNumber.ToString(), DateTime.Now.TimeOfDay.ToString());
// Some processing happens here
}
// Ah...this looks much better!
Random rnd = new Random();
for (int i = 0; i < 100; i++)
{
int randomNumber = rnd.Next();
Console.WriteLine("Generated {0} at {1}", randomNumber.ToString(), DateTime.Now.TimeOfDay.ToString());
// Some processing happens here
}Now, don't get me wrong. The Random class certainly is useful for practical randomization. For example, randomizing the order of images for an image rotator could be a good scenario for possibly using the Random class. However, if you need random numbers for the purpose of a generating a password or key, then you are much better served by using a class such as RNGCryptoServiceProvider or another class derived from RandomNumberGenerator. These classes implement a cryptographic algorithm for creating strong random values.
The moral of the story is that all random number generators are not created equal. Be sure to comprehend the differences between them. In doing so, you will be better equipped to make a decision of which one to use based upon your requirements.