I've done some research, reviewed countless SO questions that appeared similar, leading me to endless rabbit holes.
My solution is working, but I don't like it. I want to create a random IV for AES and store it with my cipher. You'll see various approaches based on the comments in my code. This final version is mostly from another source I found with some changes. What I was hoping to do is instead of generating two separate strings and concatenating them together, I could write into the same byte array and break it apart later. Goal is AES256 with random IV.
This led to errors like:
- Invalid characters in base64 string
- length of data to encrypt is invalid
- padding is invalid and cannot be removed
So here's my code. And of course, if there's a hole or ten, advice welcome. Feel free to share examples in c#. This project just happens to be in VB.net
Public Function EncryptText(input As String) As String
Dim password As String = "THISISONLYTEMPORARY"
Dim bytesToBeEncrypted As Byte() = Encoding.UTF8.GetBytes(input)
Dim passwordBytes As Byte() = Encoding.UTF8.GetBytes(password)
passwordBytes = SHA256.Create().ComputeHash(passwordBytes)
Dim result As String = AES_Encrypt(bytesToBeEncrypted, passwordBytes)
Return result
End Function
Public Function DecryptText(ByVal input As String) As String
Dim password As String = "THISISONLYTEMPORARY"
'Dim savedSalt As Byte() = Convert.FromBase64String(input.Split("%"c)(0))
'Dim savedValue As String = input.Split("%"c)(1)
Dim savedSalt As Byte() = Convert.FromBase64String(input.Substring(0, 44))
Dim savedValue As String = input.Substring(44, input.Length - 44)
Dim bytesToBeDecrypted As Byte() = Convert.FromBase64String(savedValue)
Dim passwordBytes As Byte() = Encoding.UTF8.GetBytes(password)
passwordBytes = SHA256.Create().ComputeHash(passwordBytes)
Dim result As String = AES_Decrypt(bytesToBeDecrypted, passwordBytes, savedSalt)
Return result
End Function
Public Function AES_Encrypt(ByVal bytesToBeEncrypted As Byte(), ByVal passwordBytes As Byte()) As String
Dim encryptedBytes As Byte() = Nothing
'Dim saltBytes As Byte() = New Byte() {1, 2, 3, 4, 5, 6, 7, 8}
Dim saltBytes = New Byte(31) {}
Using rng As RandomNumberGenerator = New RNGCryptoServiceProvider()
rng.GetBytes(saltBytes)
End Using
Dim saltToSave As String = Convert.ToBase64String(saltBytes)
Using ms As MemoryStream = New MemoryStream()
Using AES = New AesManaged()
AES.KeySize = 256
AES.BlockSize = 128
Dim key = New Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000)
AES.Key = key.GetBytes(CInt(AES.KeySize / 8))
AES.IV = key.GetBytes(CInt(AES.BlockSize / 8))
AES.Mode = CipherMode.CBC
AES.Padding = PaddingMode.PKCS7
Using cs = New CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write)
'cs.Write(saltBytes, 0, saltBytes.Length) ' new
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length)
cs.Close()
End Using
encryptedBytes = ms.ToArray()
End Using
End Using
Return saltToSave & Convert.ToBase64String(encryptedBytes)
End Function
Public Function AES_Decrypt(ByVal bytesToBeDecrypted As Byte(), ByVal passwordBytes As Byte(), savedSalt As Byte()) As String
Dim decryptedBytes As Byte() = Nothing
'Dim saltBytes As Byte() = New Byte() {1, 2, 3, 4, 5, 6, 7, 8}
Using ms As MemoryStream = New MemoryStream()
Using AES = New AesManaged()
AES.KeySize = 256
AES.BlockSize = 128
Dim key = New Rfc2898DeriveBytes(passwordBytes, savedSalt, 1000)
AES.Key = key.GetBytes(CInt(AES.KeySize / 8))
AES.IV = key.GetBytes(CInt(AES.BlockSize / 8))
AES.Mode = CipherMode.CBC
AES.Padding = PaddingMode.PKCS7
Using cs = New CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write)
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length)
cs.Close()
End Using
decryptedBytes = ms.ToArray()
End Using
End Using
'Return Encoding.UTF8.GetString(decryptedBytes.Skip(savedSalt.Length).ToArray())
Return Encoding.UTF8.GetString(decryptedBytes.ToArray())
End Function
I was at one point joining my IV and cipher text with a %
character and splitting those, but didn't like the visible clear separation which made the IV obvious.
Again this is working. My question is more about hardening it within the constraints that you see (A single private password and random IV) and ditching this string concatenation.
I posted this on stackoverflow but it was suggested to move to here because my solution is working. It was also suggested to not be concerned about splitting my encrypted string with "%" as you see commented out in my code and was pointed to this link: https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle