Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
Guida al Visual Basic .NET - Sicurezza e criptazione

Guida al Visual Basic .NET

Capitolo 102° - Sicurezza e criptazione

<< Precedente Prossimo >>

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 
<< Precedente Prossimo >>
A proposito dell'autore

Programmatore e analista .NET 2005/2008/2010 (in particolare C# e VB.NET), anche nell'implementazione Mono per Linux. Conoscenze approfondite di Pascal, PHP, XML, HTML 4.01/5, CSS 2.1/3, Javascript (e jQuery). Conoscenze buone di C, LUA, GML, Ruby, XNA, AJAX e Assembly 68000. Competenze basilari di C++, SQL, Hlsl, Java.