|
Anche in questo ambito, il Framework offre ottime funzionalità, procurando al programmatore molti modi per cifrare e decifrare
messaggi che non dovebbero essere intercettabili da alcun altra persona che non sia l'utente. Le classi che servono per questo scopo sono
esposte nei namespace System.Security e System.Security.Cryptography. Dopo una breve introduzione, mostrerò come sia possibile
criptare e decriptare dati usando queste potenzialità.
Introduzione alla criptazione
La criptazione è una disciplina informatica che si occupa di oscurare messaggi o dati in modo da renderli accessibili solo alle persone
alle quali sono realemnte destinati. Così facendo, si evita che qualche cracker indesiderato possa impossessarsene ed utilizzare le
informazioni ivi contenute per chissà quali scopi. Esistono tre tipi di algoritmi di criptazione:
- Simmetrici
Sono i più semplici e diretti metodi di cifratura. Per funzionare necessitano di una chiave (o password), per mezzo della quale
i dati vengono oscurati. I meccanismi interni lavorano su blocchi di bytes di dimensione prefissata, solitamente una potenza di 2: ogni algoritmo
ha una chiave di dimensioni predefinite, spesso espressa in bit. All'interno di questo tipo, ci sono due modi operandi diversi: cifrari
a blocchi e cifrari basati su stream.
I primi prendono le informazioni da criptare e le dividono in blocchi di bytes di lunghezza
pari a quella della chiave, quindi eseguono delle operazioni di Xor fra array successivi e restituiscono in output il risultato. Il primo
blocco di bytes viene cifrato sulla base di un vettore di inizializzazione, anche detto IV, ossia un'accozaglia di dati
casuali di dimensioni pari alla chiave.
I secondi generano una chiave pseudo-casuale estratta manipolando la chiave di base e la inseriscono in uno stream: prendono poi pezzi di
dati di lunghezza arbitraria e li Xorano con il contenuto dello stream
- Asimmetrici
Costituiscono i più sicuri algoritmi di criptazione, a cui devono supplire, però, tempi di elaborazione maggiori. Un algoritmo
del genere ha bisogno di una chiave pubblica, che può essere comunicata a tutti, e una chiave privata, che la persona tiene segreta.
Il messaggio viene inviato criptandolo con la chiave pubblica del destinatario, il quale poi lo decripta usando la sua personale chiave
privata. Data la complessità del loro funzionamento e la potenza di calcolo richiesta, è bene usarli solo in caso di messaggi
estremamente brevi, a favore dei più abbordabili algoritmi simmetrici
- Hash
Questi algoritmi creano una stringa di dimensiona fissa che non può essere decriptata in nessun modo: testi uguali generano output
uguali, ma non c'è maniera di risalire al messagio originale. L'unico modo per sapere se un messaggio è equivalente a quello
sottoposto all'hash consiste nel comparare i due hash
Il .Net Framework mette a disposizione wrapper per i seguenti algoritmi:
- RC2 (Cifrario Rivest): algoritmo simmetrico a blocchi da 64-bit
- DES (Data Encryption Standard): algoritmo simmetrico a blocchi da 64-bit
- 3-DES (Triple Data Encryption Standard):: algoritmo simmetrico a blocchi da 192-bit (la chiave ha dimensione maggiore rispetto al DES normale)
- AES (Advanced Encryption Standard, alias Rijndael): algoritmo simmetrico a blocchi da 256-bit
- MD5 (Message Digest Algorithm 5): hash da 128-bit
- SHA-1 (Secure Hash Standard 1): hash da 160-bit
- SHA-256, SHA-384, SHA-512 (varianti di Secure Hash Standard 2): hash da 256, 384 o 512 bit
- RSA (Rivest Shamir Adleman, dai nomi dei suoi inventori): algoritmo asimmetrico, variabile da 1024 fino a 4096 bit (di chiave)
Una prova pratica
Nei seguenti esempi, fornirò una dimostrazione del funzionamento degli algoritmi simmetrici e di hashing. Poiché i primi
derivando tutti dalla classe base SymmetricAlgorithm e i secondi da HashAlgorithm, il funzionamento illustrato per uno solo di questi può
essere ripetuto in maniera identica (eccetto che per dimensione della chiave) per ogni altro algoritmo della stessa famiglia.
Come esempio per gli algotirmi simmetrici prenderò il Rijndael (perchè mi piace il nome XD). Prima di iniziare con il codice,
bisogna sapere che ogni algoritmo è rappresentato da una classe detta provider crittografico, che di solito porta il nome
corrispondente. Questo ha il compito di immagazzinare le informazioni sulla chiave e sul blocco di dati e crare ex novo un oggetto deputato
alla criptazione o decriptazione dei messaggi. Tale oggetto implementa l'interfaccia ICryptoTransform, rappresenta una trasformazione
concreta sulle informazioni fornite e ha la funzione di convertirle effettivamente tramite il metodo TransformFinalBlock. Ecco un esempio:
Imports System.Security
Imports System.Security.Cryptography
Imports System.Text.UTF8Encoding
Module Module1
'Vettore di bytes casuali usati per oscurare la chiave:
'verrà usato nella funzione di derivazione della password
Private SaltBytes As Byte() = New Byte() _
{162, 21, 92, 34, 27, 239, 64, 30, 136, 102, 223}
'Questo è un vettore di inizializzazione per algoritmi
'simmetrici a 256-bit. Si nota, infatti, che è lungo 32 bytes
Private IV32 As Byte() = New Byte() _
{133, 206, 56, 64, 110, 158, 132, 22, _
99, 190, 35, 129, 101, 49, 204, 248, _
251, 243, 13, 194, 160, 195, 89, 152, _
149, 227, 245, 5, 218, 86, 161, 124}
'La derivazione di password è un'altra delle tecniche
'usate in criptazione: si cifra la chiave iniziale con un
'algoritmo di derivazione, fornendo come base un vettore
'di bytes casuali, chiamato salt crittografico.
'L'algoritmo applica una trasformazione sulla chiave un
'numero dato di volte (iterazioni) e restituisce alla fine una
'password di lunghezza specificata. In questo caso, poiché
'si sta utilizzando l'algoritmo Rijndael a 256 bit, sarà
'di 32 bytes
Private Function DerivePassword(ByVal Key As String) As Byte()
'Il provider crittografico
Dim Derive As Rfc2898DeriveBytes
'Il risultato dell'operazione
Dim DerivedBytes() As Byte
'Crea un nuovo provider crittografico per l'algoritmo
'di derivazione RFC2898, che ha come input Key, come
'salt crittografico l'array SaltBytes sopra definito
'e come numero di iterazioni 5. Il secondo e il terzo
'parametro sono del tutto casuali: li si può
'modificare arbitrariamente
Derive = New Rfc2898DeriveBytes(Key, SaltBytes, 5)
'Applica la trasformazione e deriva una nuova password
'ottenuta come array di 32 bytes
DerivedBytes = Derive.GetBytes(32)
Return DerivedBytes
End Function
'Data una chiave Key e un messaggio Text, usa l'algoritmo simmetrico
'a blocchi Rijndael (AES) per ottenere un insieme di dati criptato
Public Function RijndaelEncrypt(ByVal Key As String, _
ByVal Text As String) As Byte()
'Crea il nuovo provider crittografico per questo algoritmo
Dim Provider As New RijndaelManaged
'La password derivata
Dim BytePassword As Byte()
'L'oggetto che ha il compito di processare le informazioni
Dim Encryptor As ICryptoTransform
'L'output della funzione
Dim Output As Byte()
'L'input della funzione, ossia il testo convertito
'in forma binaria. Il formato UTF8 permette di
'mantenere anche i caratteri speciali come quelli accentati
Dim Input As Byte() = UTF8.GetBytes(Text)
'Imposta la dimensione della chiave
Provider.KeySize = 256
'Imposta la dimensione del blocco
Provider.BlockSize = 256
'Ottiene la password tramite derivazione dalla chiave
BytePassword = DerivePassword(Key)
'Crea un nuovo oggetto codificatore
Encryptor = Provider.CreateEncryptor(BytePassword, IV32)
'Cripta il testo
Output = Encryptor.TransformFinalBlock(Input, 0, Input.Length)
'Elimina le informazioni fornite al provider
Provider.Clear()
'Distrugge l'oggetto codificatore
Encryptor.Dispose()
Return Output
End Function
'Data una chiave Key e un messaggio cifrato Data, usa l'algoritmo
'simmetrico a blocchi Rijndael (AES) per ottenere l'insieme di
'dati di partenza
Public Function RijndaelDecrypt(ByVal Key As String, _
ByVal Data() As Byte) As String
'Crea un nuovo provider crittografico
Dim Provider As New RijndaelManaged
'La password derivata
Dim BytePassword As Byte()
'L'oggetto che ha il compito di processare le informazioni
Dim Decryptor As ICryptoTransform
'L'output della funzione in bytes
Dim Output As Byte()
Provider.KeySize = 256
Provider.BlockSize = 256
BytePassword = DerivePassword(Key)
'Ottiene l'oggetto decodificatore
Decryptor = Provider.CreateDecryptor(BytePassword, IV32)
'Tenta di decriptare il messaggio: se la chiave è
'sbagliata, lancia un'eccezione
Try
Output = Decryptor.TransformFinalBlock(Data, 0, Data.Length)
Catch Ex As Exception
Throw New CryptographicException("Criptazione fallita!")
Finally
Provider.Clear()
Decryptor.Dispose()
End Try
Return UTF8.GetString(Output)
End Function
'I dati prodotti in output sono allocati in vettori di bytes,
'ma le stringhe non sono il supporto più adatto per
'visualizzarli, poiché vengono compresi anche
'caratteri di controllo o null terminator. In ogni caso,
'la stringa sarebbe o compromessa o illeggibile (non che
'non lo debba essere). Questa funzione restituisce tutto
'il vettore come rappresentazione esadecimale in stringa
'rendendo più gradevole la vista del nostro
'magnifico messaggio cifrato
Public Function ToHex(ByVal Bytes() As Byte) As String
Dim Result As New StringBuilder
For I As Int32 = 0 To Bytes.Length - 1
'Accoda alla stringa il codice in formato esadecimale,
'facendo in modo che occupi sempre due posti, eventualmente
'pareggiando con uno zero sulla sinistra
Result.AppendFormat("{0:X2}", Bytes(I))
Next
Return Result.ToString
End Function
Sub Main()
Dim Input, Output As String
Dim Key As String
Console.WriteLine("Inserire un testo qualsiasi:")
Input = Console.ReadLine
Console.WriteLine("Inserire una chiave di criptazione:")
Key = Console.ReadLine
Try
Output = ToHex(RijndaelEncrypt(Key, Input))
Console.WriteLine()
Console.WriteLine("Il testo criptato è:")
Console.WriteLine(Output)
Catch CE As CryptographicException
Console.WriteLine("Password errata!")
End Try
Console.ReadKey()
End Sub
End Module
E questo per l'Hash:
Imports System.Security
Imports System.Security.Cryptography
Imports System.Text.UTF8Encoding
Module Module2
'Questa semplice funzione genera un hash MD5
Public Function GetMd5(ByVal Text As String) As Byte()
Dim Input As Byte() = UTF8.GetBytes(Text)
Dim Output As Byte()
'MD5.Create() crea un nuovo provider crittografico per l'hash Md5
Output = MD5.Create().ComputeHash(Input, 0, Input.Length)
Return Output
End Function
Sub Main()
Dim Input As String
Console.WriteLine("Inserire un testo qualsiasi:")
Input = Console.ReadLine
Console.WriteLine()
Console.WriteLine("Il suo hash è:")
Console.WriteLine(ToHex(GetMd5(Input)))
Console.ReadKey()
End Sub
End Module |
|