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.