Troubleshooting Tips
LANGUAGES: VB .NET
ASP.NET VERSIONS: 1.0 | 1.1
Can You Keep a Secret?
Use the DPAPI to easily encrypt data.
By Don Kiely
When you need to encrypt data, there is nothing easier than the Data Protection API (DPAPI). In other installments in this series I've introduced the DPAPI and explained how it works as part of operating systems starting with Windows 2000 to manage encryption keys for you. That's the hardest thing to do in encryption and where many apps create gaping security holes - even though they are using strong encryption. You have to keep your secrets secret!
This time I'll show a simple example of using DPAPI. This is a bit of a conundrum, since DPAPI is a Windows API with no implementation in the .NET Framework. But instead of messy p/invoke calls to the Win32 API, you can use a .NET component available at http://www.gotdotnet.com, a Microsoft site with lots of sample code, discussions, and workspaces for development projects. Go there, select User Samples from the Toolbox, and search for DPAPI. You can download either C# or VB .NET versions of the component.
To keep things simple, I'll use a WinForms app for this first example. In the weeks to come, I'll move it over to ASP.NET, but this way we can focus on the details of DPAPI. The sample code you can download here has the simple UI shown in Figure 1 to display the clear and encrypted text (see end of article for details).
Figure 1. Display clear and encrypted text.
Here's the code for the Encrypt button:
Private Sub cmdEncrypt_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles cmdEncrypt.Click
If txtClear.Text.Length = 0 Then
txtError.Text = "Please enter clear text to encrypt."
txtClear.Focus()
Exit Sub
End If
LoadDP()
Try
Dim clearText As Byte() = _
Encoding.ASCII.GetBytes(txtClear.Text)
Dim encryptedText As Byte()
Dim hwnd As IntPtr
If chkPromptUser.Checked Then
hwnd = Me.Handle
Else
hwnd = IntPtr.Zero
End If
If txtEntropy.Text.Length > 0 Then
Dim entropy As Byte() = _
Encoding.ASCII.GetBytes(txtEntropy.Text)
encryptedText = dp.Encrypt(clearText, entropy, hwnd)
Else
encryptedText = dp.Encrypt(clearText, hwnd)
End If
txtEncrypted.Text = Convert.ToBase64String(encryptedText)
lblEncryptedLength.Text = "Length: " & _
txtEncrypted.Text.Length
lblClearLength.Text = ""
txtError.Text = ""
txtClear.Text = ""
Catch ex As Exception
txtError.Text = ex.Message & Environment.NewLine & _
ex.InnerException.Message
End Try
End Sub
Most of the code is doing UI stuff. The real work starts with the call to the LoadDP method, which merely instantiates the DPAPI component for the appropriate key store. The next few lines grab the clear text as an array of bytes and get a handle to the current form if we're going to prompt the user for an additional user name and password. You can also optionally include entropy text, some additional salt to make it harder to break the encryption.
Encryption happens in this line:
encryptedText = dp.Encrypt(clearText, hwnd)
The rest of the code converts the encryptedText to a base64 string and displays it on the form. That's it! No key management, no need for deep understanding of the black magic of encryption. And most importantly, no need to manage and hide your own keys. DPAPI and Windows takes care of that for you.
Turning the encrypted text into clear text is just about as simple:
Private Sub cmdDecrypt_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles cmdDecrypt.Click
If txtEncrypted.Text.Length = 0 Then
txtError.Text = "Please enter encrypted text to decrypt."
txtEncrypted.Focus()
Exit Sub
End If
LoadDP()
Try
Dim decryptedText As Byte()
Dim encryptedText As Byte() = _
Convert.FromBase64String(txtEncrypted.Text)
Dim hwnd As IntPtr
If chkPromptUser.Checked Then
hwnd = Me.Handle
Else
hwnd = IntPtr.Zero
End If
If txtEntropy.Text.Length > 0 Then
Dim entropy As Byte() = _
Encoding.ASCII.GetBytes(txtEntropy.Text)
decryptedText = _
dp.Decrypt(encryptedText, entropy, hwnd)
Else
decryptedText = dp.Decrypt(encryptedText, hwnd)
End If
txtClear.Text = Encoding.ASCII.GetString(decryptedText)
lblClearLength.Text = "Length: " & txtClear.Text.Length
lblEncryptedLength.Text = ""
txtError.Text = ""
txtEncrypted.Text = ""
Catch ex As Exception
txtError.Text = ex.Message & Environment.NewLine & ex.InnerException.Message
End Try
End Sub
Again, most of the code is doing UI stuff, and the decryption happens in this statement:
decryptedText = dp.Decrypt(encryptedText, hwnd)
DPAPI automatically uses the same key used to encrypt the text; all you have to specify is the entropy -if any was used - and allow the user to enter a username and password, if they were used to encrypt the data.
Voil ! Encryption made easy. Next time I'll talk about the DPAPI key storage options and how they affect encryption.
The samples referenced in this article are available for download.
Don Kiely is senior technology consultant for Information Insights, a business and technology consultancy in Fairbanks, Alaska. E-mail him at mailto:[email protected].