O .NET já conta com a classe System.Random, que pode ser usada para gerar números (pseudo) aleatórios de qualidade suficientepara a maioria das aplicações que você pode precisar. Mas os números gerados pela System.Random podem não ser adequados para alguns casos, por exemplo em criptografia, pois eles são previsíveis, no sentido de que não são realmente aleatórios: uma função matemática é utilizada para gerá-los, a partir de um número inicial (seed). Se a mesma seed for utilizada, a mesma sequência de números será gerada. Para os casos onde sejam necessários números realmente aleatórios, pode ser utilizada a classe RNGCryptoServiceProvider do .NET, mas sua utilização não é tão fácil e intuitiva quanto a System.Random.

A classe abaixo herda de System.Random, mas modifica a funcionalidade para que a mesma funcione com a RNGCryptoServiceProvider por baixo dos panos, completa inclusive com buffer de números gerados para melhorar a performance.

 

public class SecureRandom : Random
  {
    private RNGCryptoServiceProvider rng;
    byte[] buffer;
    int bufferend = 0;

    public SecureRandom()
      : this(32768)
    {

    }

    public SecureRandom(int bufsize)
    {
      buffer = new byte[bufsize];
      rng = new RNGCryptoServiceProvider();
      RefillBuffer();
    }

    private void RefillBuffer()
    {
      var new_array = new byte[buffer.Length - bufferend];
      rng.GetBytes(new_array);
      Array.Copy(new_array, 0, buffer, bufferend, new_array.Length);
      bufferend = buffer.Length;
    }

    public override int Next()
    {
      if (bufferend < 4)
      {
        RefillBuffer();
      }

      bufferend -= 4;
      return BitConverter.ToInt32(buffer, bufferend);
    }

    public override int Next(int minValue, int maxValue)
    {
      int gen;
      do
      {
        gen = Next();
      } while (gen < minValue || gen >= maxValue);
      return gen;
    }

    public override int Next(int maxValue)
    {
      return Next(0, maxValue);
    }

    public override void NextBytes(byte[] buf)
    {
      int toFill = buf.Length;
      int actualindex = 0;
      while (toFill > 0)
      {
        if (toFill > bufferend)
        {
          if (bufferend > 0)
          {
            Array.Copy(buffer, 0, buf, actualindex, bufferend);
            actualindex += bufferend;
            toFill -= bufferend;
            bufferend = 0;
          }
          RefillBuffer();
        }
        else
        {
          Array.Copy(buffer, bufferend - toFill, buf, actualindex, toFill);
          bufferend -= toFill;
          toFill = 0;
        }
      }
    }

    protected override double Sample()
    {
      // Step 1: fill an array with 8 random bytes       
      var bytes = new Byte[8];
      NextBytes(bytes);
      // Step 2: bit-shift 11 and 53 based on double's mantissa bits 
      var ul = BitConverter.ToUInt64(bytes, 0) / (1 << 11);
      Double d = ul / (Double)(1UL << 53);
      return d;
    }

    public override double NextDouble()
    {
      return Sample();
    }
  }
Anúncios