I wrote this for use internally at work and have never worked with encrypted data before, so can you please critique? It's written in VB.NET which I have decent experience in, just not the encryption portion of it.
I have written a simple internal messaging client. Basically a home grown email program but it uses file saves over the local network and never goes online. It also writes all data after encryption only so nothing is ever stored somewhere decrypted except volatile RAM. We don't want our users to have access to outside communication and we want it all encrypted due to HIPAA/PCI. We're a small collections company and want to inexpensively make communication easier internally without opening large risk or letting people get to the outside internet.
I'm storing this as XML data. I have one config file (also encrypted) which contains all the settings and user info. So name and password and userID, etc. When they try to log in, I decrypt the config file and look at the XML to see if their password is right. If it is, I let them into the program and decrypt their individual inbox file which is also XML encrypted with this same routine.
I have one 32 byte key that is saved into my source code and not shared with anyone else in the world but me. And then each file gets a unique IV every time its encrypted or re-encrypted and that IV is also saved to the file along with the encrypted data. I'm saving the bytes to the file as hexadecimal.
Here's the module the handles reading/writing files:
Imports System.Security.Cryptography
Imports System.IO
Imports System.Text
Module encryption
Public Sub TESTING_in_encryption()
End Sub
Public Function ObfuscateString(str As String) As String
'NOT ENCRYPTION! Just stops casual observers from reading the plain text
Return System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(str))
End Function
Public Function DeObfuscateString(str As String) As String
'NOT ENCRYPTION! Just stops casual observers from reading the plain text
Return System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(str))
End Function
Public Sub EncryptFile_Aes(fullpath As String, plaintext As String)
Dim cryptString As String
'encrypt string
cryptString = EncryptString_Aes(plaintext)
Using sw As StreamWriter = New StreamWriter(fullpath, False)
sw.Write(cryptString)
sw.Close()
End Using
End Sub
''' <summary>
''' Encrypts data with the hardcoded key and new IV
''' </summary>
''' <param name="plainText">Readable string to be encrypted</param>
''' <returns>Returns bytes as string with first 16 being the IV</returns>
''' <remarks></remarks>
Public Function EncryptString_Aes(ByVal plainText As String) As String
Dim sb As StringBuilder = New StringBuilder()
' Create an AesCryptoServiceProvider object
' with the specified key and IV.
Using aesAlg As New AesCryptoServiceProvider()
aesAlg.Key = aesKey
aesAlg.GenerateIV()
' Create a decrytor to perform the stream transform.
Dim encryptor As ICryptoTransform = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV)
' Create the streams used for encryption.
Dim msEncrypt As New MemoryStream()
Using csEncrypt As New CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)
Using swEncrypt As New StreamWriter(csEncrypt)
'Write all data to the stream.
swEncrypt.Write(plainText)
End Using
sb.Append(BitConverter.ToString(aesAlg.IV))
sb.Append(BitConverter.ToString(msEncrypt.ToArray))
'For Each b As Byte In aesAlg.IV
' sb.Append(BitConverter.ToString({b}))
'Next
'For Each b As Byte In msEncrypt.ToArray
' sb.Append(b.ToString("000"))
'Next
End Using
End Using
Return sb.ToString.Replace("-", "")
End Function 'EncryptStringToBytes_Aes
Public Function DecryptFile_Aes(ByVal fullpath As String) As String
Using sr As StreamReader = New StreamReader(fullpath)
Dim answerValue As String = DecryptString_Aes(sr.ReadToEnd)
sr.Close()
Return answerValue
End Using
End Function
''' <summary>
''' Pass a string of contiguous bytes with leading 16 IV
''' </summary>
''' <param name="hexString"></param>
''' <returns>Plain text string</returns>
''' <remarks></remarks>
Public Function DecryptString_Aes(ByVal hexString As String) As String
If hexString.Length = 0 Then
Return ""
End If
Dim plaintext As String = Nothing
Dim iv(15) As Byte
Dim cryptBytes As Byte() = {0}
Dim itemindex As Integer = 0
For i As Integer = 1 To Len(hexString) Step 2
If itemindex <= 15 Then
iv(itemindex) = Byte.Parse(Mid(hexString, i, 2), Globalization.NumberStyles.HexNumber)
Else
ReDim Preserve cryptBytes(itemindex - 16)
cryptBytes(itemindex - 16) = Byte.Parse(Mid(hexString, i, 2), Globalization.NumberStyles.HexNumber)
End If
itemindex += 1
Next
Using aesAlg As New AesCryptoServiceProvider()
aesAlg.Key = aesKey
aesAlg.IV = iv
' Create a decrytor to perform the stream transform.
Dim decryptor As ICryptoTransform = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV)
' Create the streams used for decryption.
Using msDecrypt As New MemoryStream(cryptBytes)
Using csDecrypt As New CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)
Using srDecrypt As New StreamReader(csDecrypt)
' Read the decrypted bytes from the decrypting stream
' and place them in a string.
plaintext = srDecrypt.ReadToEnd()
End Using
End Using
End Using
End Using
Return plaintext
End Function 'DecryptString_Aes
End Module
First: Any obvious things I'm doing wrong or not thinking of?
Second: It doesn't take a very big "inbox" for it to take a really long time to decrypt when a user logs in. If your inbox file is 2+ MB then it can literally take a couple minutes. I assume if I stored it differently so I didn't have to decrypt all or nothing it would be better, but short of that because I'm trying to keep this as simple as possible, how can I improve speed?
Keep in mind, these are XML files I'm using, so you can imagine them as just simple text files if that's easier.
Module
and not aClass
? If you clutter your code withModule
definitions everywhere you also clutter intellisense. APublic Class
would be much more useable, as well. \$\endgroup\$