There's a description of a suitable algorithm in the Java nextInt
documentation.
The algorithm repeatedly rejects any values that will cause an uneven distribution and tries again. This means, theoretically, that in the worst-case scenario it could loop forever. In reality it will be pretty quick.
Here's a (completely untested) C# translation:
public static int GetNextInt32(this RNGCryptoServiceProvider rng, int maxValue)
{
if (maxValue < 1)
throw new ArgumentOutOfRangeException("maxValue", maxValue, "Value must be positive.");
var buffer = new byte[4];
int bits, val;
if ((maxValue & -maxValue) == maxValue) // is maxValue an exact power of 2
{
rng.GetBytes(buffer);
bits = BitConverter.ToInt32(buffer, 0);
return bits & (maxValue - 1);
}
do
{
rng.GetBytes(buffer);
bits = BitConverter.ToInt32(buffer, 0) & 0x7FFFFFFF;
val = bits % maxValue;
} while (bits - val + (maxValue - 1) < 0);
return val;
}