Introduction
This is a sample application that will encrypt and decrypt files in VB.NET using Rijndael Managed. I started this project because I had several files on my computer that I didn’t want accessible to anyone but me. Sure I could have downloaded a free encryption program off the net, but what fun would that be? So being the hobbyist that I am, I decided to create my own.
Background
Here are some brief descriptions of the cryptographic concepts relevant to this application. I am going to keep things as simple and basic as possible. If you want further details there is a ton of information on the web. Also check out “.NET Encryption Simplified” by: wumpus1 right here at Code Project. These descriptions are based on how the concepts are used in this application.
- The Key:
The password used to encrypt/decrypt data.
- The IV:
Initialization Vector. This is used to encrypt the first portion of the data to be encrypted.
- Rijndael:
The algorithm used for encryption/decryption. In this application, Rijndael is using a 256 bit key and a 128 bit IV.
- SHA512 Hashing:
This takes a string (the password) and transforms it into a fixed size (512 bits) of “encrypted data”. The same string will always “hash” into the same 512 bits of data.
Using the code
In this section I will cover the following:
- Creating the Key
- Creating the IV
- Encryption and decryption
- Changing file extensions
- Putting it all together
My explanations will be brief because my code is heavily commented. Before we begin, we will need the following Imports
statements:
Imports System
Imports System.IO
Imports System.Security
Imports System.Security.Cryptography
Now let's declare our global variables:
Dim strFileToEncrypt As String
Dim strFileToDecrypt As String
Dim strOutputEncrypt As String
Dim strOutputDecrypt As String
Dim fsInput As System.IO.FileStream
Dim fsOutput As System.IO.FileStream
Creating the Key
Of course, the most secure Key would be a randomly generated Key. But I prefer to make up my own. The following code is a function that will create a 256 bit Hashed Key from the user’s password. Here is what happens in the function:
- The function receives a string (the password).
- Converts the string to an array.
- Converts the array to a byte.
- Uses SHA512 to hash the byte.
- Stores the first 256 bits of the hashed byte into a new byte (the key).
- Returns the key.
For a more in-depth description, read the ‘comments' in the following code:
Private Function CreateKey(ByVal strPassword As String) As Byte()
Dim chrData() As Char = strPassword.ToCharArray
Dim intLength As Integer = chrData.GetUpperBound(0)
Dim bytDataToHash(intLength) As Byte
For i As Integer = 0 To chrData.GetUpperBound(0)
bytDataToHash(i) = CByte(Asc(chrData(i)))
Next
Dim SHA512 As New System.Security.Cryptography.SHA512Managed
Dim bytResult As Byte() = SHA512.ComputeHash(bytDataToHash)
Dim bytKey(31) As Byte
For i As Integer = 0 To 31
bytKey(i) = bytResult(i)
Next
Return bytKey
End Function
Here is an alternate example of creating a Key without using SHA512 hashing:
Private Function CreateKey(ByVal strPassword As String) As Byte()
Dim bytKey As Byte()
Dim bytSalt As Byte() = System.Text.Encoding.ASCII.GetBytes("salt")
Dim pdb As New PasswordDeriveBytes(strPassword, bytSalt)
bytKey = pdb.GetBytes(32)
Return bytKey
End Function
Creating the IV
OK, so we used the first 256 bits of our hashed byte to create the key. We will use the next 128 bits of our hashed byte to create our IV. This way the key will be different from the IV. This function is almost identical to the previous one.
Private Function CreateIV(ByVal strPassword As String) As Byte()
Dim chrData() As Char = strPassword.ToCharArray
Dim intLength As Integer = chrData.GetUpperBound(0)
Dim bytDataToHash(intLength) As Byte
For i As Integer = 0 To chrData.GetUpperBound(0)
bytDataToHash(i) = CByte(Asc(chrData(i)))
Next
Dim SHA512 As New System.Security.Cryptography.SHA512Managed
Dim bytResult As Byte() = SHA512.ComputeHash(bytDataToHash)
Dim bytIV(15) As Byte
For i As Integer = 32 To 47
bytIV(i - 32) = bytResult(i)
Next
Return bytIV
End Function
Here is an alternate example of creating an IV without using SHA512 hashing:
Private Function CreateIV(ByVal strPassword As String) As Byte()
Dim bytIV As Byte()
Dim bytSalt As Byte() = System.Text.Encoding.ASCII.GetBytes("salt")
Dim pdb As New PasswordDeriveBytes(strPassword, bytSalt)
bytIV = pdb.GetBytes(16)
Return bytIV
End Function
Encryption and Decryption
Here is the meat and potatoes of the encryption/decryption process. This block of code has been modified from an article called “Tales from the Crypto” by Billy Hollis. Here is what happens in this procedure:
- Define the enumeration for
CryptoAction
(encrypt/decrypt).
- Begin with an encrypted/unencrypted file.
- Use a
FileStream
object to open and read the file.
- Use a
CryptoStream
object to perform encryption/decryption.
- Use another
FileStream
object to write the encrypted/decrypted file.
For a more in-depth description read the ‘comments' in the following code:
Private Enum CryptoAction
ActionEncrypt = 1
ActionDecrypt = 2
End Enum
Private Sub EncryptOrDecryptFile(ByVal strInputFile As String, _
ByVal strOutputFile As String, _
ByVal bytKey() As Byte, _
ByVal bytIV() As Byte, _
ByVal Direction As CryptoAction)
Try
fsInput = New System.IO.FileStream(strInputFile, FileMode.Open, _
FileAccess.Read)
fsOutput = New System.IO.FileStream(strOutputFile, _
FileMode.OpenOrCreate, _
FileAccess.Write)
fsOutput.SetLength(0)
Dim bytBuffer(4096) As Byte
Dim lngBytesProcessed As Long = 0
Dim lngFileLength As Long = fsInput.Length
Dim intBytesInCurrentBlock As Integer
Dim csCryptoStream As CryptoStream
Dim cspRijndael As New System.Security.Cryptography.RijndaelManaged
pbStatus.Value = 0
pbStatus.Maximum = 100
Select Case Direction
Case CryptoAction.ActionEncrypt
csCryptoStream = New CryptoStream(fsOutput, _
cspRijndael.CreateEncryptor(bytKey, bytIV), _
CryptoStreamMode.Write)
Case CryptoAction.ActionDecrypt
csCryptoStream = New CryptoStream(fsOutput, _
cspRijndael.CreateDecryptor(bytKey, bytIV), _
CryptoStreamMode.Write)
End Select
While lngBytesProcessed < lngFileLength
intBytesInCurrentBlock = fsInput.Read(bytBuffer, 0, 4096)
csCryptoStream.Write(bytBuffer, 0, intBytesInCurrentBlock)
lngBytesProcessed = lngBytesProcessed + _
CLng(intBytesInCurrentBlock)
pbStatus.Value = CInt((lngBytesProcessed / lngFileLength) * 100)
End While
csCryptoStream.Close()
fsInput.Close()
fsOutput.Close()
Changing File Extensions
Basically, what we are doing here is taking the path name of the file to encrypt/decrypt and adding or removing an “.encrypt” extension. This is not really necessary for encrypting files, but I think it looks cool.
When encrypting a file we would add an “.encrypt” extension as follows:
OpenFileDialog.FileName = ""
OpenFileDialog.Title = "Choose a file to encrypt"
OpenFileDialog.InitialDirectory = "C:\"
OpenFileDialog.Filter = "All Files (*.*) | *.*"
If OpenFileDialog.ShowDialog = DialogResult.OK Then
strFileToEncrypt = OpenFileDialog.FileName
txtFileToEncrypt.Text = strFileToEncrypt
Dim iPosition As Integer = 0
Dim i As Integer = 0
While strFileToEncrypt.IndexOf("\"c, i) <> -1
iPosition = strFileToEncrypt.IndexOf("\"c, i)
i = iPosition + 1
End While
strOutputEncrypt = strFileToEncrypt.Substring(iPosition + 1)
Dim S As String = strFileToEncrypt.Substring(0, iPosition + 1)
strOutputEncrypt = strOutputEncrypt.Replace("."c, "_"c)
txtDestinationEncrypt.Text = S + strOutputEncrypt + ".encrypt"
When decrypting a file we would remove the “.encrypt” extension as follows:
OpenFileDialog.FileName = ""
OpenFileDialog.Title = "Choose a file to decrypt"
OpenFileDialog.InitialDirectory = "C:\"
OpenFileDialog.Filter = "Encrypted Files (*.encrypt) | *.encrypt"
If OpenFileDialog.ShowDialog = DialogResult.OK Then
strFileToDecrypt = OpenFileDialog.FileName
txtFileToDecrypt.Text = strFileToDecrypt
Dim iPosition As Integer = 0
Dim i As Integer = 0
While strFileToDecrypt.IndexOf("\"c, i) <> -1
iPosition = strFileToDecrypt.IndexOf("\"c, i)
i = iPosition + 1
End While
strOutputDecrypt = strFileToDecrypt.Substring(0, _
strFileToDecrypt.Length - 8)
Dim S As String = strFileToDecrypt.Substring(0, iPosition + 1)
strOutputDecrypt = strOutputDecrypt.Substring((iPosition + 1))
txtDestinationDecrypt.Text = S + strOutputDecrypt.Replace("_"c, "."c)
Keep in mind that the above code is geared towards my sample application.
Putting It All Together
OK, so this is where everything comes together with the “Encrypt” and “Decrypt” buttons. Basically what happens here is:
- Variables are declared for the Key and IV.
- The user’s password is passed to the
CreateKey
function.
- The user’s password is passed to the
CreateIV
function.
- The input path name, output path name, Key, IV, and
CryptoAction
are passed to the EncryptOrDecryptFile
procedure.
Encrypting would go as follows:
Dim bytKey As Byte()
Dim bytIV As Byte()
bytKey = CreateKey(txtPassEncrypt.Text)
bytIV = CreateIV(txtPassEncrypt.Text)
EncryptOrDecryptFile(strFileToEncrypt, txtDestinationEncrypt.Text, _
bytKey, bytIV, CryptoAction.ActionEncrypt)
Decrypting would go as follows:
Dim bytKey As Byte()
Dim bytIV As Byte()
bytKey = CreateKey(txtPassDecrypt.Text)
bytIV = CreateIV(txtPassDecrypt.Text)
EncryptOrDecryptFile(strFileToDecrypt, txtDestinationDecrypt.Text, _
bytKey, bytIV, CryptoAction.ActionDecrypt)
Points of Interest
This application uses the Rijndael algorithm to encrypt and decrypt files. You could also use Data Encryption Standard (DES) or Triple DES to do the same. All you would need to change is the Key size, IV size and the Crypto Service Provider. I hope that you can have some fun with this code. Questions and comments are appreciated.
History
- October 28, 2005: Posted.
- October 31, 2005: File size issue fixed, thanks to Moonark.
- November 1, 2005: Alternate examples of
CreateKey/IV
added, thanks to Vlad Tepes.
- November 4, 2005: Array issue fixed, thanks to ccady.