Can You Keep a Secret?

Use the DPAPI to easily encrypt data.

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].

 

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish