Keeping Secrets: A Guide to VB .NET Cryptography

he indisputable appeal of Web services will drive most organizations to evaluate development platforms such as .NET. However, security concerns have always made organizations reluctant to embrace new technologies. Fortunately, well-proven security and network technologies such as virtual private networks (VPNs) and firewalls can improve the security and performance of Web service applications tremendously?and free developers from having to implement still-evolving, XML-based security technologies into their applications.

Despite computer security’s fundamental importance, few code examples show how to use the .NET security and cryptography classes. Look in the indexes of most .NET books and you won’t find any entries for security, much less cryptography.

In this article, I explain how to use the .NET encryption and key-generating classes in your own VB programming. I also provide a working utility that encrypts or decrypts files. With this utility, you can easily maintain a secret file on your hard drive that, for example, holds all your password/username pairs, your various investments, and any other information that you want to keep private. You also could encrypt files before sending them to others over the Internet. Encryption has many uses, and you can customize the utility program to suit your special needs (e.g., adding batch file processing and so on).

Beware the Two Great Dangers
The two great computer security dangers are attacks and peeping. An attack can be a virus that attempts to delete files, slow down your computer, or cause some other damage. Peeping is usually more passive (you may never notice it), but it is a violation of your privacy: someone reads your data by getting access to your hard drive or intercepts messages you send over the Internet. Peeping can also include an attack if the intruder modifies the data that they read.

The best defense against peeping is cryptography. Encrypt your files effectively and you protect them from both peeping and modification by intruders. Encryption is also sometimes used to authenticate communications: users who know the secret password likely are who they say they are.

No security measures against peeping are totally foolproof, however, because someone else can potentially learn your secret password. I found this out the hard way when a friend bought the same model of fire safe that I did. The manufacturer must have produced only a few different keys, because his key fit my safe. Using his own key, he discovered the 1 oz. gold coins I had in my safe, stole them, and provided his girlfriend at the time with some nice presents.

The Requirements for .NET Cryptography
To tap into the .NET security features, you need Imports statements and encryption packs. First, to experiment with any of the code in this article, be sure that you add the following Imports statements at the top of your Visual Basic code window:

Imports System.IOImports System.TextImports System.Security.Cryptography

Second, note that the U.S. government restricted encryption in the past to prevent certain encryption technology from being exported. Although the restrictions are no longer in effect, the .NET framework prohibits "strong" or "high" encryption in export versions of the Windows OS. If you don't already have strong encryption capabilities in your version of Windows, you can download an update on the Microsoft Web site. Install the High Encryption Pack included in Service Pack 2 for Windows 2000, or Service Pack 6a for NT. Internet Explorer 5.5 also includes the High Encryption Pack for users of Windows ME, 95, and 98.

The Utility Encryptor/Decryptor Program
You can use the included utility to encrypt and decrypt files ( click here for the full code). If you want to jump right in and start hiding the secrets in some of your files, just fire up the utility.

 
Figure 1 | Click here to get a close-up view of the utility encryptor/decryptor program.

The utility surrounds the encryption, decryption, and key-generating procedures with some user interface conveniences. It provides a TextBox into which the user types a filename and another TextBox to type in the key. In the top TextBox of the utility, provide the path to the file that you want to encrypt. In the one below, type in an eight-character password (see Figure 1). The utility creates a new, encrypted file in the same folder as the original. This new file is given the same name as the original, except xx is appended. For example, MyFile.txt becomes MyFilexx.txt.

You don't have to delete the original file (MyFile.txt), but presumably you will since the whole point of encrypting is to conceal the data in the original file. To restore the plaintext file, enter MyFilexx.txt in the top TextBox and provide the password you used to encrypt it. The utility will then create a new, decrypted file named MyFile.txt that is identical to the original. In other words, the utility recognizes the appended xx on a filename as its cue to decrypt the ciphertext (the encrypted version of the original plaintext data).

Warning: If you encrypt a file and then forget the password you used to encrypt it, you won't be able to restore the file (decrypt it). Also, it's best to use a password that includes digits and special characters (like the pound sign), along with alphabetic characters. If you use only English words as obvious as your dog's name, any relative could get into your secret files.

Although Microsoft offers more than one type of encryption in .NET, the type I explore is symmetric encryption. Symmetric encryption, also known as private key encryption, uses the same key (and essentially the same process) to encrypt and decrypt. Both the encryptor and decryptor must keep the key secret. (In public key, asymmetric encryption, two keys are generated and one of them is made public. This technique is considered the strongest current encryption technology, but it is quite a bit slower than symmetric encryption.)

Before actually encrypting with the .NET encryption classes, you must generate a key from the password a user provides. You can generate the key using a hash function. Hashing in cryptography converts a user's password string into an unrecognizable mish-mash of what look like (and ideally should be) random bits. The mish-mash can be used as a key, which is then employed in the encryption process as a way of uniquely distorting the data.

For example, one way to use a key to encrypt would be to add the ASCII values of the key to the ASCII values of the data:

Key: ab = ASCII: 97, 98Data: merry = ASCII: 109, 101, 114, 114, 121

So, when you add the values (and repeat the key as necessary), you get the encryption:

   97	    98	    97	    98	   97	 +109	  +101	  +114	  +114	 +121  206      199	   211	   212	  218

Hashing always produces the same bit pattern if you provide it with identical data (thus the same password will always generate the same key, if you use the same hashing algorithm). In fact, you can test this process in the example code provided with this article by using the ComputeHash method of the .NET SHA1CryptoServiceProvider class. Any time you provide the word morph to this method, for example, you'll reliably get back the following hash: 124, 230, 93, 253, 197, 206, 136, 72. So will anyone else who knows that morph is the secret word.

Determining Your Key Length
The .NET encryption routines expect the keys that you use to be a particular size. For example, the DES (Data Encryption Standard) function wants a key to be 64 bits long, while the Rijndael algorithm wants 128-, 192-, or 256-bit keys?all other things being equal, the longer the key, the stronger the encryption. So if you decide to use an algorithm other than DES, you can find out which key sizes it permits by querying the LegalKeySizes property. You can get the MinSize (the smallest key size permitted), the MaxSize (the largest), and the SkipSize (the increment). SkipSize indicates any sizes available between the minimum and maximum sizes. For instance, the SkipSize for the Rijndael algorithm is 64 bits.

You can use the following code to find out the key sizes:

'create the DES Crypto object des As New DESCryptoServiceProvider() Dim fd() As KeySizesfd = des.LegalKeySizes() 'tells us the size(s), in bitsMsgBox("minsize = " & fd(0).MinSize & Chr(13) & "maxsize = " & fd(0).MaxSize & 
Chr(13) & "skipsize = " & fd(0).SkipSize)

If you run this code, you'll get 64, 64, 0. But if you change the declaration to TripleDESCryptoServiceProvider() , you'll get 128, 192, 64.

Note: The DES specification asks for an eight-byte password, but employs only a 56-bit key (seven bytes). The least significant bit in each byte is thrown away (it's used as a parity bit, but is not used in the actual encryption process).

The following code generates the password in the example program:

 Public Class Form1     Inherits System.Windows.Forms.Form      'create an 8-byte long array to hold the key     Private TheKey(7) As Byte      'Stuff some random values into the vector:    Private Vector() As Byte = {&H12, &H44, &H16, &HEE, &H88, &H15, &HDD, &H41}

First, it defines two variables to hold the key and the initialization vector (which I'll explain shortly). You need only provide random values to the vector, though you could hash a password or some other variable to get values for the vector. In any case, the following procedure creates the key out of a password the user types in:

Sub CreateKey(ByVal strKey As String) ' Byte array to hold keyDim arrByte(7) As Byte Dim AscEncod As New ASCIIEncoding()Dim i As Integer = 0AscEncod.GetBytes(strKey, i, strKey.Length, arrByte, i)  'Get the hash value of the passwordDim hashSha As New SHA1CryptoServiceProvider() Dim arrHash() As Byte = hashSha.ComputeHash(arrByte)  'put the hash value into the keyFor i = 0 To 7TheKey(i) = arrHash(i)Next i End Sub

The user's password (strKey) is passed to this procedure, and then the password is separated into individual ASCII values held in a byte array. This byte array is fed to the ComputeHash method of the SHA1CryptoServiceProvider class, which returns a hash value. You put the hash into your TheKey array for use later in the encryption (or decryption) procedures.

Note that SHA1CryptoServiceProvider can actually provide 160 bits, but you're using only 64 in the example program. If you wish, you could employ 64 of the unused bits for the initialization vector. The user should then enter a 16-byte key though.

Just what does that initialization vector do? It is a byte array with eight elements, just like the key. The vector, however, doesn't work like the key. The vector is designed to eliminate a problem typical of block ciphers like DES, where the text is broken into eight-byte groups, which are each then manipulated as units. DES uses the patterns of data from each previous block to further distort the next block. Change a single character in the first block of the plaintext and you change the characters that follow it in all the blocks to come. Thus you've eliminated the problem of creating duplicate blocks down the chain of connected blocks.

For example, even if in a fit of passion you sent a message filled with nothing but "Melanie! Melanie! Melanie! Melanie," the combination of the key and the impact of previous blocks on subsequent blocks would prevent duplication in the ciphertext. However, if you think about it, the very first part of the message is vulnerable to repetition if the same key is used over and over for multiple messages in which the greeting is also repeated (more on that shortly). For that reason, initialization vectors are used to simulate previous-block chaining.

The following code from the sample utility illustrates how to encrypt a file:

 Sub  Encrypt(ByVal  inName As  String , ByVal  outName As  String )            Try             Dim  storage(4096) As  Byte  'create buffer             Dim  totalBytesWritten As  Long  = 8 'Keeps track 
of bytes written.   Dim packageSize As Integer 'Specifies the number
of bytes written at one time.   'Declare the file streams. Dim fin As New FileStream(inName, FileMode.Open,FileAccess.Read) Dim fout As New FileStream(outName, FileMode.OpenOrCreate, _ FileAccess.Write) fout.SetLength(0)   Dim totalFileLength As Long = fin.Length 'Specifies the size
of the source file.   'create the Crypto object Dim des As New DESCryptoServiceProvider()   Dim crStream As New CryptoStream(fout, _ des.CreateEncryptor(TheKey, Vector), _ CryptoStreamMode.Write)     'flow the streams While totalBytesWritten < totalFileLength packageSize = fin.Read(storage, 0, 4096) crStream.Write(storage, 0, packageSize) totalBytesWritten = Convert.ToInt32(totalBytesWritten +
packageSize / des.BlockSize * des.BlockSize)

End While   crStream.Close()     Catch e As Exception MsgBox(e.Message) End Try   End Sub

Note the three streams created: file in (fin , the plaintext original data), file out (fout , the encrypted result), and crStream (a cryptostream that feeds the results of the DES encryption to the output file fout ). The virtue of the cryptostream is that you don't need to store its results in some intermediate file or buffer. The streamed output can simply be fed to the input of some other object.

The only fundamental difference between this encryption procedure and the decryption procedure is that when you decrypt, you use a different method (CreateDecryptor ) of the DESCryptoServiceProvider object. Otherwise the process (the arguments, the streams, and so on) is identical going in either the encryption or decryption direction.

Prevent Repetition Searches and Brute Force Attacks
Hackers or cryptanalyst employ two common methods to attack encrypted files. One is looking for repetition in your ciphertext and the other is a brute force search for your key. Let's first consider how the initialization vector prevents repetitions, and then explore why a stronger algorithm may be your best bet for defending against brute force searches.

Cryptanalysts search for patterns?particularly repeating patterns?as they try to break a cipher. Unfortunately, people commonly start their communications in repetitive ways (Dear Sir, From the Desk of, and so on). If you begin multiple messages with the same phrase and you use the same key with each one, the start of the ciphertext will be identical for each message. If each encrypted message from Antonio Banderas begins with the greeting @4^F ([email protected], a spy would be rather dull not to test and see if the first words were Dear Melanie. An important procedure when deciphering secret messages is guessing some of the words that likely will be used in the message. Don't give a cryptanalyst this advantage. In the example code, the initialization vector's bits are padded at the beginning of your message, which solves the repetition problem. Only the beginning of a message is vulnerable in this way.

Because computers are far faster and more accurate than humans, they are particularly adept at repetitive tasks like trying every possible key combination to crack your key. What's more, the DES encryption algorithm is, itself, not secret. The method it uses to encrypt data was made public back in the 1970s. And, of course, an intruder who wants to automate his or her search for your key can easily employ the .NET DESCryptoServiceProvider class.

How long would it take your computer to test every possible combination of the 128 bits in the key/vector combination? Experts disagree on a length of time. Some claim it would take months, while others say that a six-figure computer with specialized hardware could test billions of keys per second and crack a DES ciphertext in hours. If you have enemies willing to spend months or hundreds of thousands worth of cash to find out your secrets, I suggest you switch to TripleDES or some other algorithm. TripleDES, as you might guess, asks for a password three times as long as the eight-character DES password. The resulting key is therefore 192 bits, rather than the 64-bit DES key. Remember, all other things being equal, the longer the key, the stronger the encryption.

To further confound intruders, the TripleDES process divides the 192 bits into three separate 64-bit keys, which it uses to encrypt the data with the first key, decrypt it using the second key, and finally encrypt it again with the third key.

Don't Stop with Cryptography
Now that you've seen how .NET DES cryptography works, you could further experiment with other .NET security features, including the extremely interesting public key techniques. Although they execute slowly, they probably can offer even greater security than TripleDES. I personally don't have secrets important enough to resort to more than simple DES, but your encryption needs may be different.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: