Imports System.Security.Cryptography
''' <summary>
''' Questa classe deve essere tenuta nascosta. Serve per generare un codice
''' dato un nome utente.
''' </summary>
Public Class CodeGenerator
Private _UserName As String
Private _CodeLength As Int16 = 16
Private _DerivingIterations As Int16 = 5
Private _SaltBytes As Byte()
Private _Code As String
''' <summary>
''' Nome utente da usare. Ad un nome utente sarà associato un solo codice.
''' </summary>
Public Property UserName() As String
Get
Return _UserName
End Get
Set(ByVal value As String)
_UserName = value
_Code = Nothing
End Set
End Property
''' <summary>
''' Lunghezza del codice generato, in bytes. Dato che il codice viene scritto in esadecimale,
''' ad ogni bytes corrispondono due cifre. Perciò il codice finale avrà lunghezza doppia.
''' </summary>
Public Property CodeLength() As Int16
Get
Return _CodeLength
End Get
Set(ByVal value As Int16)
If value >= 8 Then
_CodeLength = value
Else
_CodeLength = 8
End If
_Code = Nothing
End Set
End Property
''' <summary>
''' Salt crittografico. Questo array di bytes viene usato per derivare il codice finito.
''' E' il punto di forza dell'algoritmo, in quanto ogni programmatore che usa questa classe
''' può utilizzare un array di bytes diverso da tutti gli altri, e ciò produrra anche un
''' risultato diverso. Questo sorgente, quindi, è riusabile al 100%.
''' </summary>
Public Property SaltBytes() As Byte()
Get
Return _SaltBytes
End Get
Set(ByVal value As Byte())
_SaltBytes = value
_Code = Nothing
End Set
End Property
''' <summary>
''' Numero di iterazioni compiute dall'algoritmo di derivazione. Anche questo è un fattore
''' cambiando il quale cambia il risultato.
''' </summary>
Public Property DerivingIterations() As Int16
Get
Return _DerivingIterations
End Get
Set(ByVal value As Int16)
If value >= 2 Then
_DerivingIterations = value
Else
_DerivingIterations = 2
End If
_Code = Nothing
End Set
End Property
''' <summary>
''' Code generato alla fine del procedimento.
''' </summary>
Public ReadOnly Property Code() As String
Get
If _Code Is Nothing Then
Me.CalculateCode()
End If
Return _Code
End Get
End Property
'Trovate una spiegazione breve dell'algoritmo di derivazione sulla mia guida
Private Sub CalculateCode()
Dim Derive As Rfc2898DeriveBytes
Dim DerivedBytes() As Byte
Dim Result As New System.Text.StringBuilder
Derive = New Rfc2898DeriveBytes(Me.UserName, SaltBytes, Me.DerivingIterations)
DerivedBytes = Derive.GetBytes(Me.CodeLength)
For I As Int16 = 0 To Me.CodeLength - 1
Result.AppendFormat("{0:X2}", DerivedBytes(I))
Next
_Code = Result.ToString
End Sub
''' <summary>
''' Divide il codice generato in blocchi più piccoli da X caratteri ciascuno.
''' </summary>
''' <param name="Separator">Stringa da inserire tra ciascun blocco.</param>
''' <param name="ByteCounts">Questo array contiene la numerosità di ciascun blocco.</param>
''' <remarks>Se ad esempio, si ha il codice "AB4F89D" e si imposta Seperator="-" e
''' ByteCounts su {1,3,3}, si otterrà la stringa "A-B4F-89D".</remarks>
Public Function GetFormattedCode(ByVal Separator As String, ByVal ParamArray ByteCounts As Int16()) As String
Dim CodeReader As New IO.StringReader(Me.Code)
Dim Buffer() As Char
Dim Result As New System.Text.StringBuilder
For I As Int16 = 0 To ByteCounts.Length - 1
ReDim Buffer(ByteCounts(I) - 1)
CodeReader.ReadBlock(Buffer, 0, ByteCounts(I))
Result.Append(Buffer)
If I < ByteCounts.Length - 1 Then
Result.Append(Separator)
End If
Next
Return Result.ToString
End Function
End Class
'Esempio
Module AutenticationModule
Sub Main()
Dim C As New CodeGenerator
'8 bytes -> 16 caratteri
C.CodeLength = 8
'Array di bytes assolutamente casuali: vedrete che anche cambiando di 1
'l'ultimo byte si otterrà un codice completamente diverso.
C.SaltBytes = New Byte() {90, 2, 189, 250, 78, 3, 12, 67}
'Legge l'UserName
Console.Write("UserName = ")
C.UserName = Console.ReadLine
'Scrive il codice generato, formattato in questo modo:
'XXXX - XXXX - XXXX - XXXX
Console.Write("Code = {0}", C.GetFormattedCode(" - ", 4, 4, 4, 4))
Console.ReadKey()
End Sub
End Module