Search code examples
vb.netsecuritycryptographyencryption-symmetric

In VB.NET, writing AES key and encrypted data to same MemoryStream not working


I'm attempting to use symmetric encryption to encrypt some data, and store the key necessary to decrypt with the data in a MemoryStream. (While I know that this alone is really dumb security-wise, I'm going to use RSA to encrypt the symmetric key. Right now, though, I'm trying to just get this part to work.)

I'm using the FileHelpers library to parse the data for my delimiter (the semicolon, as I don't believe I'll ever have a semicolon in the data). Unfortunately, in my decryption function, when it's parsing, it only returns back one record. And, if I show the entire string of encrypted data created at the end of this function, it doesn't appear to be using multiple records.

I'm wondering if when I create the new cryptostream, it's defaulting to the beginning of the memory stream, so when I write my encrypted data, it overwrites the data I had just written to the memory stream. Do you think that's right?

Thanks for your help!

Private Function Encrypt(ByVal data As String) As String
    Dim utf8 As New UTF8Encoding
    ' Convert string data to byte array
    Dim inBytes() As Byte = utf8.GetBytes(data)
    ' Create memory stream for storing the data we've manipulated
    Dim ms As New MemoryStream()

    Dim aes As New RijndaelManaged()
    aes.KeySize = 256

    Dim cs As New CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write)

    ' Write key to beginning of memory stream
    ms.Write(aes.Key, 0, aes.Key.Length)
    ' Add semicolon delimiter to memory stream
    ms.Write(utf8.GetBytes(";"), 0, utf8.GetBytes(";").Length)
    ' Write IV to memory stream
    ms.Write(aes.IV, 0, aes.IV.Length)
    ' Write semicolon delimiter to memory stream
    ms.Write(utf8.GetBytes(";"), 0, utf8.GetBytes(";").Length)
    ' Ensure that the data we've just written is in the memory stream, before
    ' we add the encrypted data
    ms.Flush()

    ' Write the encrypted data
    cs.Write(inBytes, 0, inBytes.Length) ' encrypt
    cs.FlushFinalBlock()

    ' Return encrypted data as string
    Return Convert.ToBase64String(ms.GetBuffer(), 0, ms.Length)
End Function

Solution

  • Here is a quick sample of how you can achieve this. I have included both the Encrypt and Decrypt because it is obviously important that you follow the same pattern on both sides. I will leave the relevant error checking etc. to you :)

    Imports System.Security.Cryptography
    Imports System.IO
    Imports System.Text
    
    Module Module1
    
      Sub Main()
        Dim encrypted As String = Encrypt("Here is the data")
        Dim decrypted As String = Decrypt(encrypted)
        Console.WriteLine(decrypted)
        Console.ReadKey()
      End Sub
    
      Private Function Encrypt(clearText As String) As String
        Dim aes As New RijndaelManaged
        aes.KeySize = 256
    
        Using ms As New MemoryStream
          ms.WriteByte(aes.Key.Length)
          ms.Write(aes.Key, 0, aes.Key.Length)
          ms.WriteByte(aes.IV.Length)
          ms.Write(aes.IV, 0, aes.IV.Length)
    
          Using cs As New CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write)
            Dim bytes() As Byte = Encoding.UTF8.GetBytes(clearText)
            cs.Write(bytes, 0, bytes.Length)
          End Using
    
          Return Convert.ToBase64String(ms.ToArray())
        End Using
      End Function
    
      Private Function Decrypt(cipherText As String) As String
        Dim ms As New MemoryStream(Convert.FromBase64String(cipherText))
    
        Dim keyLength As Byte = ms.ReadByte()
        Dim key(keyLength - 1) As Byte
        ms.Read(key, 0, keyLength)
    
        Dim ivLength As Byte = ms.ReadByte()
        Dim iv(ivLength - 1) As Byte
        ms.Read(iv, 0, ivLength)
    
        Dim dataOffset As Integer = ms.Position
    
        Dim aes As New RijndaelManaged()
        aes.Key = key
        aes.IV = iv
    
        Using cs As New CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read)
          Using sr As New StreamReader(cs, Encoding.UTF8)
            Return sr.ReadToEnd()
          End Using
        End Using
      End Function
    End Module