What's New
Q & A
Tip Jar
C# Helper...
Follow VBHelper on Twitter Follow VBHelper on Twitter
MSDN Visual Basic Community
TitleMake a extension methods that encrypt and decrypt strings in Visual Basic .NET
DescriptionThis example shows how to make a extension methods that encrypt and decrypt strings in Visual Basic .NET.
Keywordscryptography, extentions, extension method, files, encrypt, decrypt, cipher, encipher, decipher, AES
The .NET cryptography methods work most naturally with streams or byte arrays not strings. To make working with strings easier, this program defines extension methods that convert between byte arrays and strings containing hexadecimal values. Then when a program encrypts a string into an array of bytes, it can display the result as a hexadecimal string.
' Convert a byte array into a readable string of
' hexadecimal values.
<Extension()> _
Public Function ToHex(ByVal the_bytes() As Byte) As String
    Return ToHex(the_bytes, False)
End Function
<Extension()> _
Public Function ToHex(ByVal the_bytes() As Byte, ByVal _
    add_spaces As Boolean) As String
    Dim result As String = ""
    Dim separator As String = ""
    If (add_spaces) Then separator = " "
    For i As Integer = 0 To the_bytes.Length - 1
        result &= the_bytes(i).ToString("x2") & separator
    Next i
    Return result
End Function

' Convert a string containing 2-digit hexadecimal values
' into a byte array.
<Extension()> _
Public Function ToBytes(ByVal the_string As String) As _
    Dim the_bytes As New List(Of Byte)()
    the_string = the_string.Replace(" ", "")

    For i As Integer = 0 To the_string.Length - 1 Step 2
        the_bytes.Add( _
            Byte.Parse(the_string.Substring(i, 2), _
    Next i
    Return the_bytes.ToArray()
End Function
The real cryptographic work is done by the CryptBytes method.
' Encrypt or decrypt the data in in_bytes[] and return the
' result.
Public Function CryptBytes(ByVal password As String, ByVal _
    in_bytes() As Byte, ByVal encrypt As Boolean) As Byte()
    ' Make an AES service provider.
    Dim aes_provider As New AesCryptoServiceProvider()

    ' Find a valid key size for this provider.
    Dim key_size_bits As Integer = 0
    For i As Integer = 1024 To 1 Step -1
        If (aes_provider.ValidKeySize(i)) Then
            key_size_bits = i
            Exit For
        End If
    Next i
    Debug.Assert(key_size_bits > 0)
    Console.WriteLine("Key size: " & key_size_bits)

    ' Get the block size for this provider.
    Dim block_size_bits As Integer = aes_provider.BlockSize

    ' Generate the key and initialization vector.
    Dim key() As Byte = Nothing
    Dim iv() As Byte = Nothing
    Dim salt() As Byte = {&H0, &H0, &H1, &H2, &H3, &H4, _
        &H5, &H6, &HF1, &HF0, &HEE, &H21, &H22, &H45}
    MakeKeyAndIV(password, salt, key_size_bits, _
        block_size_bits, key, iv)

    ' Make the encryptor or decryptor.
    Dim crypto_transform As ICryptoTransform
    If (encrypt) Then
        crypto_transform = _
            aes_provider.CreateEncryptor(key, iv)
        crypto_transform = _
            aes_provider.CreateDecryptor(key, iv)
    End If

    ' Create the output stream.
    Using out_stream As New MemoryStream()
        ' Attach a crypto stream to the output stream.
        Using crypto_stream As New CryptoStream(out_stream, _
                crypto_transform, CryptoStreamMode.Write)
            ' Write the bytes into the CryptoStream.
            crypto_stream.Write(in_bytes, 0, _
            Catch ex As CryptographicException
                ' Ignore this exception. The password is
                ' bad.
                ' Re-throw this exception.
            End Try

            ' return the result.
            Return out_stream.ToArray()
        End Using
    End Using
End Function
The basic idea is to make a cryptographic service provider and attach it to a stream. As you write into the streamm, the provider automatically encrypts or decrypts the data. The details are in creating and initializing the provider.

The method creates a new AesCryptoServiceProvider to use the AES encryption method.

Next the program must make a key and initialization vector (IV) to initialize the service provider. It starts by finding a supported key size. It starts with a key size of 1,024 and reduces it until the provider's ValidKeySize method returns true. The key size you get will depend on things such as which version of Windows you are using.

Note: If you will encrypt and decrypt files on different computers, they must be able to use the same key size. You may need to set this value yourself if one computer can use a larger key than the other.

The program then calls the MakeKeyAndIV method described shortly to create a key and IV. The salt is an array of pseudo-random bytes that you initialize to make breaking the code with a dictionary attack harder. Pick your own set of values for this, don't use the values shown here.

The method then creates an encryptor or decryptor, depending on whether it must encrypt or decrypt the file.

The rest of the method looks messy but is straightforward. It makes an output MemoryStream, associates it with the encryptor/decryptor, and then writes into the stream.

The following code shows the MakeKeyAndIV method.
' Use the password to generate key bytes.
Private Sub MakeKeyAndIV(ByVal password As String, ByVal _
    salt() As Byte, ByVal key_size_bits As Integer, ByVal _
    block_size_bits As Integer, ByRef key() As Byte, ByRef _
    iv() As Byte)
    Dim derive_bytes As New Rfc2898DeriveBytes(password, _
        salt, 1000)

    key = derive_bytes.GetBytes(key_size_bits / 8)
    iv = derive_bytes.GetBytes(block_size_bits / 8)
End Sub
This code creates a new Rfc2898DeriveBytes object, passing its constructor your password, salt, and an iteration number. The object applies its operation the indicated number of times to make its result "more random." In this case, it applies a pseudo-random number generator based on the HMACSHA1 algorithm 1000 times to generate its bytes.

The method then uses the object's GetBytes methods to get the key and IV that the program needs to initialize the cryptographic service provider.

Very Important Note: Never store a password inside a program. If you do, then a clever attacker can break open your program, read the password, and decrypt whatever the program wants to keep secret. This is particularly easy for .NET programs where it's relatively easy to read the program's IL code. A much better approach is to make the user enter the password at run time.

Also note that the password must match exactly to decode a file. If the password is off by even a single character, the result will be complete gibberish.
Copyright © 1997-2010 Rocky Mountain Computer Consulting, Inc.   All rights reserved.