Similmente alla gerarchia vista con le classi Hash, Cryptography definisce una classe astratta di nome RandomNumberGenerator, dalla quale tutte le altre classi random number generator devono derivare. Attualmente, .NET mette a disposizione una sola implementazione di RandomNumberGenerator : RNGCryptoServiceProvider.

RandombNumberGenerator ha solo due metodi che si possono utilizzare per generare valori random: GetBytes() e GetNonZeroBytes(). Entrambe prendono un array di byte come loro unico argomento. GetNonZeroBytes() fa proprio quello che dice il suo nome: Nessuno degli elementi dell'array conterrà un valore zero (0). Mentre invece, GetBytes() potrà contenerli.

Il codice che segue mostra il funzionamento di entrambe i metodi

RandomNumberGenerator rng = new RNGCryptoServiceProvider();
byte[] randomData = new byte[100];
rng.GetBytes(randomData);
byte[] randomDataWithNoZeros = new byte[50];
rng.GetNonZeroBytes(randomDataWithNoZeros);

L'oggetto RandomNumberGenerator legge la lunghezza dell'array per determinare quanti dati dovrà generare.

Custom Construct di RNGCryptoServiceProvider

RNGCryptoServiceProvider ha tre costruttori custom che permettono di scegliere quale provider si vuole utilizzare. Due di questi costruttori utilizzano dei semplici valori - un array di byte e una stringa - in questo momento questi metodi non usano i valori immessi. L'altro costruttore usa un oggetto basato su CspParameters. Questa classe permette la definizione di quale provider si vuole utilizzare, attraverso un key container name.

Per esempio, il seguente codice, utilizza il tipo di provider RSA (definito come PROV_RSA_FULL costante CryptoAPI, la quale equivale ad 1).

const int PROV_RSA_FULL = 1;
CspParameters csp = 
new CspParameters(PROV_RSA_FULL);
RandomNumberGenerator rng = 
new RNGCryptoServiceProvider(csp);

La costante CryptoApi (come PROV_RSA_FULL) non sono definite in .NET; bisogna definirle da soli. I loro valori possono essere trovati all'interno del file wincrypt.h . Si può scegliere di specificare il nome del provider ed il loro key container name:

CspParameters csp = new CspParameters(1,
   "Microsoft Enhanced Cryptographic Provider v1.0", "Administrator");
RandomNumberGenerator rng = 
new RNGCryptoServiceProvider(csp);

Se si sbaglia a specificare le informazioni, la creazione di  CspParameters  verrà effettuata con successo, ma il suo successivo utilizzo solleverà una eccezione come dimostrato qui di seguito.

CspParameters csp = new CspParameters(10000,
   "Microsoft Enhanced Cryptographic Provider v1.0", "Administrator");
   
// questo solleverà una eccezione, visto che non esiste un provider di tipo 10000
   
RandomNumberGenerator rng = new RNGCryptoServiceProvider(csp);

Formattere le informazionI RNG

Molto probabilmente vi sarete accorti che le classi Cryptography adorano i byte di array  . Per esempio, i metodi GetBytes() e GetNonZeroBytes() lavorano unicamente con gli array di Byte. Lo stesso fa il metodo Write() della classe hash astratta HashAlgorithm. Probabilmente, questo può non essere quello che si vuole. Cosa si deve fare per generare un insieme di valori random di tipo integer o più genericamente un valore di tipo .NET System.Int32? .NET mette a disposizione alcune classi che ci vengono in aiuto che per formattare i valori Byte in altri tipi di dati.

Prendiamo per esempio la classe BinaryReader presente nello spazio di nomi System.IO. Visto che BinaryReader è un oggetto Stream based, siamo in grado di usare la nostra conoscenza degli stream (almeno si spera  )per formattare i valori nel modo che vogliamo.

Il modo migliore per capirlo è dare uno sguardo al codice seguente.

RandomNumberGenerator rng = new RNGCryptoServiceProvider();
byte[] randomData = new byte[18];
rng.GetBytes(randomData);
BinaryReader binReader = 
new BinaryReader(new MemoryStream(randomData));
int randomValue = binReader.ReadInt32();

Come si può vedere, tutto quello di cui si ha bisogno sono due righe di codice per avere un valore random.  Quando si chiama il metodo ReadInt32(), lo stream crea un valore integer di 4-byte e muove la posizione corrente dello stream in posizione sul 4byte. Attenzione ora. L'array di byte randomData non è sempre divisible per 4, quindi se si continua a chiamare ReadInt32(), si potrebbe finire in out of data.Di conseguenza, se si vuole avere il valore di ritorno dalla chiamata GetBytes() sotto forma di una serie di valori integer (System.Int32) è bene ricordarsi di "wrappare" l'eccezione EndOfStreamException come dimostrato qui di seguito.

// E' bene considerare che randoData contiene di già valori Random
BinaryReader binReader = new BinaryReader(new MemoryStream(randomData));
try
{
   
int randomValue;
   
do
   
{
      randomValue = binReader.ReadInt32();
   } 
while(true);
}
catch(EndOfStreamException eose) {}

Quando si esegue questo codice e viene visualizzato il valore di randomValue, si avrà 821499462, 1586377564, 573104213 e -772757365. Allo stesso modo si vedrà che l'eccezione EndOfStreamException è stata sollevata, questo è accaduto quando ho provato a chiamare a generare un numero generico per la quinta volta.

Ci sono altri metodi di formattazione in BinaryReader, come ReadString() e ReadDouble(), è bene tenere a mente questi metodi quando si lavora con le classi Cryptography e si ha bisogno di generare valori random sotto forma di diversi tipi .NET.