6.6. Encrypt Secrets for the Current User
Applications often need a way to store
private data in a file or in memory. The obvious solution is
symmetric encryption, which scrambles your data
using a random series of bytes called a secret
key. The problem is that when you want to decrypt your
scrambled data, you need to use the same secret key you used to
encrypt. This introduces serious complications. Either you need to
find a secure place to safeguard your secret key (which is tricky at
best), or you need to derive the secret key from some other
information, like a user-supplied password (which is much more
insecure, and can break down entirely when users forget their
passwords).
Note: Need a quick way to encrypt secret information, without
needing to worry about key management? The long awaited solution
appears in . NET 2.0 with the ProtectedData class.
The ideal solution is to have the Windows operating system encrypt
the data for you. To accomplish this, you need the
DPAPI (Data Protection API), which
encrypts data using a symmetric key that's based on
a piece of user-specific or machine-specific information. This way,
you don't need to worry about key storage or
authentication. Instead, the operating system authenticates the user
when he logs in. Data stored by one user is automatically
inaccessible to other users.In previous versions of .NET, there were no managed classes for using
the DPAPI. This oversight is corrected in .NET 2.0 with the new
ProtectedData class in the
System.Security.Cryptography namespace.
6.6.1. How do I do that?
The ProtectedData class provides two shared
methods. ProtectData( ) takes a
byte array with source data and returns a byte array with encrypted
data. UnprotectData( )
performs the reverse operation, taking an encrypted byte array and
returning a byte array with the decrypted data.The only trick to using the ProtectData( ) and
UnprotectData( ) methods is that you can only
encrypt or decrypt data in a byte array. That means that if you want
to encrypt strings, numbers, or something else, you need to write it
to a byte array before you perform the encryption.To see this in action, you can run the console application code in
Example 6-6.
Example 6-6. Storing an encrypted string of text in a file
Imports System.Security.CryptographyWhen you run this application, you'll be prompted to
Imports System.IO
Module ProctedData
Sub Main( )
' Get the data.
Console.WriteLine("Enter a secret message and press enter.")
Console.Write(">")
Dim Input As String = Console.ReadLine( )
Dim DataStream As MemoryStream
If Input <> " Then
Dim Data( ), EncodedData( ) As Byte
' Write the data to a new MemoryStream.
DataStream = New MemoryStream( )
Dim Writer As New StreamWriter(DataStream)
Writer.Write(Input)
Writer.Close( )
' Convert the MemoryStream into a byte array,
' which is what you need to use the ProtectData( ) method.
Data = DataStream.ToArray( )
' Encrypt the byte array.
EncodedData = ProtectedData.Protect(Data, Nothing, _
DataProtectionScope.CurrentUser)
' Store the encrypted data in a file.
My.Computer.FileSystem.WriteAllBytes("c:\secret.bin",
EncodedData, False)
End If
End Sub
End Module
type in some text, which will be encrypted using your current user
account information and stored in the file
secret.bin. The data won't be
accessible to any other user.To verify that the data is encrypted, you have two choices. You can
open the file and take a look for yourself, or you can modify the
code so that it reads the data directly from the encrypted memory
stream. This code tries the latter, and displays a string of
meaningless gibberish as a result:
' Verify the data is encrypted by reading and displaying itTo decrypt the data, you need to place it into a byte array and then
' without performing any decryption.
DataStream = New MemoryStream(EncodedData)
Dim Reader As New StreamReader(DataStream)
Console.WriteLine("Encrypted data: " & Reader.ReadToEnd( ))
Reader.Close( )
use the UnprotectData( ) method. To extract your
data out of the unencrypted byte array, you can use a
StreamReader. To add decryption support to the
previous example, insert the following code, which opens the file and
displays the secret message that you entered earlier:
If My.Computer.FileSystem.FileExists("c:\secret.bin") ThenRemember, because the data is encrypted using the current user
Dim Data( ), EncodedData( ) As Byte
EncodedData = My.Computer.FileSystem.ReadAllBytes("c:\secret.bin")
Data = ProtectedData.Unprotect(EncodedData, Nothing, _
DataProtectionScope.CurrentUser)
Dim DataStream As New MemoryStream(Data)
Dim Reader As New StreamReader(DataStream)
Console.WriteLine("Decoded data from file: " & Reader.ReadToEnd( ))
Reader.Close( )
End If
profile, you can decrypt the data at any time. The only restriction
is that you need to be logged on under the same user account.Note that when you protect data, you must choose one of the values
from the DataProtectionScope
enumeration. There are two choices:
Note: No matter which DataProtectionScope you choose, the
encrypted information will be stored in a specially protected area of
the Windows registry.
LocalMachine
Windows will encrypt data with a machine-specific key, guaranteeing
that no one can read the data unless they log in to the same
computer. This works well for server-side applications that run
without user intervention, such as Windows services and web services.
CurrentUser
Windows will encrypt data with a user-specific key, so that
it's inaccessible to any other user.
In the current example, user-specific data is stored. However, you
could modify the DataProtectionScope to store data
that's accessible to any user on the current
computer.
6.6.2. What about...
...protecting data before you put it in a database? Once you use the
ProtectedData class to encrypt your data, you can
put it anywhere you want. The previous example wrote encrypted data
to a file, but you can also write the binary data to a database
record. To do so, you simply need a binary field in your table with
enough room to accommodate the encrypted byte array. In SQL Server,
you use the varbinary data type.