Posts with tag: Cryptography

If you have used Random() in C# to any extent before you probably know it isen't really that random, and can produce quite predictable results. Especially when it's being used in threads or parallel loops. I had such a scenario recently where I needed to have a better random number generator that even when used on different threads or from different instances still produced random numbers.

So how do you get a better random number than what Random() can provide?
Easy answer is you use the RNGCryptoServiceProvider. The purpose of this class is to create cryptographically strong random data. Perfect for the task at hand.

Here is the code I created for generating random integers between a given interval. I'm using the RNGCryptoServiceProvider to produce 4 bytes of random data that I then convert to an integer. The produced integer is somewhere between -int.Max and int.Max. So I reduce the size of the number by the proportion of the size of the range wanted as compared to the full range of an Int32 and then take the absolute value of that.

public int GetRealRandom(int min = 0, int max = int.MaxValue)
{
    if (max <= min)
        throw new ArgumentOutOfRangeException("max paramater must be greater than min paramater");

    int result = 0;
    using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
    {
        byte[] data = new byte[4];
        rng.GetBytes(data);
        int value = BitConverter.ToInt32(data, 0);
        result = value;

        var proportion = (max - min + 0d) / int.MaxValue;
        result = Math.Abs((int) Math.Round((result*proportion)));
        result += min;
    }
    return result;
}

And to validate it you can use this Unit Test to check the result. Note that this test might fail if you dont do enough iterations to produce a big enough data set to test it on.

[TestMethod]
public void GenerateRealRandom()
{
    var min = 0;
    var max = 1000;

    var pn = new List<int>();
    foreach (var r in Enumerable.Range(0, 100000))
    {
        var number = GetRealRandom(min, max);
        pn.Add(number);
        Assert.IsTrue(number <= max && number >= min);
    }

    var average = pn.Average();
    var minVal = pn.Min();
    var maxVal = pn.Max();
    var middle = (max - min) / 2;

    Assert.IsTrue(average < middle + 1 && average > middle - 1);
    Assert.IsTrue(minVal <= min + 1);
    Assert.IsTrue(maxVal >= max - 1);
}