Encryption AES
AES Encryption/Decryption class with HMAC validation and automatic IV embedding.
This class provides methods to securely encrypt and decrypt strings using AES symmetric encryption.
A new random IV is generated during encryption and prepended to the ciphertext.
An HMAC (SHA-256) is computed over IV + ciphertext and prepended as well, to ensure data integrity and authenticity.
The decryption routine validates the HMAC before decrypting.
VB
Imports System.Security.Cryptography
Imports System.Text
Imports System.IO
Public NotInheritable Class EncryptionAes
Private ReadOnly aes As Aes
Private ReadOnly hmacKey As Byte()
''' <summary>
''' Initializes a new instance of the EncryptionAes class using a password-derived key.
''' </summary>
''' <category>Methods</category>
''' <param name="password">The password from which to derive both AES and HMAC keys.</param>
''' <remarks>
''' The AES key and HMAC key are derived from the password using SHA-256 hashing.
''' </remarks>
Public Sub New(ByVal password As String)
aes = Aes.Create()
aes.Key = DeriveKey(password, aes.KeySize \ 8)
hmacKey = DeriveKey(password & "-hmac", 32) ' 256-bit HMAC key
End Sub
''' <summary>
''' Encrypts the specified plain text using AES and prepends the IV and HMAC to the result.
''' </summary>
''' <category>Methods</category>
''' <param name="plainText">The plain text string to encrypt.</param>
''' <returns>A Base64-encoded string that includes the HMAC, IV, and encrypted data.</returns>
Public Function EncryptData(ByVal plainText As String) As String
aes.GenerateIV()
Dim iv As Byte() = aes.IV
Dim plainBytes = Encoding.UTF8.GetBytes(plainText)
Using ms As New MemoryStream()
Using cs As New CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write)
cs.Write(plainBytes, 0, plainBytes.Length)
cs.FlushFinalBlock()
End Using
Dim cipherBytes = ms.ToArray()
' Combine IV + ciphertext
Dim combined(iv.Length + cipherBytes.Length - 1) As Byte
Buffer.BlockCopy(iv, 0, combined, 0, iv.Length)
Buffer.BlockCopy(cipherBytes, 0, combined, iv.Length, cipherBytes.Length)
' Compute HMAC over IV + ciphertext
Dim hmacBytes As Byte()
Using hmac As New HMACSHA256(hmacKey)
hmacBytes = hmac.ComputeHash(combined)
End Using
' Final output = HMAC + IV + ciphertext
Dim finalData(hmacBytes.Length + combined.Length - 1) As Byte
Buffer.BlockCopy(hmacBytes, 0, finalData, 0, hmacBytes.Length)
Buffer.BlockCopy(combined, 0, finalData, hmacBytes.Length, combined.Length)
Return Convert.ToBase64String(finalData)
End Using
End Function
''' <summary>
''' Decrypts the specified Base64-encoded string that includes HMAC, IV, and encrypted data.
''' </summary>
''' <category>Methods</category>
''' <param name="encryptedText">The Base64-encoded string to decrypt.</param>
''' <returns>The decrypted plain text string.</returns>
''' <exception cref="CryptographicException">Thrown if HMAC validation fails.</exception>
Public Function DecryptData(ByVal encryptedText As String) As String
Dim fullData = Convert.FromBase64String(encryptedText)
Dim hmacLength = 32 ' SHA-256 output size
Dim ivLength = aes.BlockSize \ 8
' Extract HMAC, IV, and ciphertext
Dim hmacOriginal(hmacLength - 1) As Byte
Dim iv(ivLength - 1) As Byte
Dim cipherText(fullData.Length - hmacLength - ivLength - 1) As Byte
Buffer.BlockCopy(fullData, 0, hmacOriginal, 0, hmacLength)
Buffer.BlockCopy(fullData, hmacLength, iv, 0, ivLength)
Buffer.BlockCopy(fullData, hmacLength + ivLength, cipherText, 0, cipherText.Length)
' Recompute HMAC
Dim combined(iv.Length + cipherText.Length - 1) As Byte
Buffer.BlockCopy(iv, 0, combined, 0, iv.Length)
Buffer.BlockCopy(cipherText, 0, combined, iv.Length, cipherText.Length)
Using hmac As New HMACSHA256(hmacKey)
Dim hmacComputed = hmac.ComputeHash(combined)
If Not hmacComputed.SequenceEqual(hmacOriginal) Then
Throw New CryptographicException("HMAC validation failed. Data may have been tampered with.")
End If
End Using
aes.IV = iv
Using ms As New MemoryStream()
Using cs As New CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write)
cs.Write(cipherText, 0, cipherText.Length)
cs.FlushFinalBlock()
Return Encoding.UTF8.GetString(ms.ToArray())
End Using
End Using
End Function
''' <summary>
''' <c>PRIVATE</c> - Derives a fixed-length cryptographic key from a password using SHA-256.
''' </summary>
''' <category>Methods</category>
''' <param name="input">The input string (typically a password).</param>
''' <param name="length">The desired length of the key in bytes.</param>
''' <returns>A byte array containing the derived key.</returns>
''' <remarks>
''' The key is derived from the SHA-256 hash of the input, truncated or padded to the specified length.
''' </remarks>
Private Function DeriveKey(ByVal input As String, ByVal length As Integer) As Byte()
Using sha256 As SHA256 = SHA256.Create()
Dim hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(input))
Dim key(length - 1) As Byte
Array.Copy(hash, key, length)
Return key
End Using
End Function
End Class