Encrypt DataSets for Offline Storage, Page 2
Start the overview of the wrapper module from the top and work your way to the bottom:
- The Store enumeration is used to determine whether the key is associated with the computer or the user. If the key is associated with the computer, any user can decrypt the data.
- The private class Consts uses a GUID for the entropy value. Using a GUID ensures that the entropy value is unique. You can create a unique GUID in VS.NET from the Tools|Create GUID menu.
- The private Win32 class is used for convenience to import the DPAPI methods and LocalFree. You used the DllImportAttribute to introduce the API methods, but VB.NET supports the Declare syntax for importing API methods too. The two constants are CRYPTPROTECT_UI_FORBIDDEN and CRYPTPROTECT_LOCAL_MACHINE. The former constant prevents a DPAPI prompt from being displayed, and the latter constant is Or'd into the flags argument passed to the DPAPI methods, if the Store.Machine enumeration value is passed to the wrapper methods.
- The DATA_BLOB structure represents the data argument that is passed to the DPAPI methods. The StructLayoutAttribute permits you to control the physical layout of the DATA_BLOB structure. LayoutKind.Sequential means that the data is laid out in the order it appears.
- The most interesting methods are the Encrypt and Decrypt wrapper methods. Encrypt and Decrypt are very similar in implementation except that Decrypt ultimately calls CryptProtectData and Decrypt calls CryptUnprotectData. The following is a basic summarization of the steps each method employs:
- The flags argument is initialized.
- SetBlobData is called to convert the input string into a DATA_BLOB; memory is dynamically allocated in SetBlobData.
- SetBlobData is called to fill in the DATA_BLOB for the entropy value.
- After the data and entropy value are copied into a DATA_BLOB, the DPAPI encryption or decryption methods are called.
- If the API method succeeds, the encrypted or decrypted bytes are converted and returned as a string.
- Because dynamically allocated memory must be released, SetBlobData is called in a try block and it allocates memory using Marshal.AllocHGlobal. This memory is freed in a finally block using Marshal.FreeHGlobal.
- (LocalFree is used by GetBlobData.)
Writing an Encrypted FileStream
Naturally, the next step in storing your encrypted offline data is writing the encrypted data to a file. The .NET framework supports this. Listing 4 demonstrates the code necessary to both write and read a file using a StreamWriter and StreamReader.
Listing 4: Writing to and reading from a file stream
Public Shared Function WriteToFile(ByVal encrypted As String) As String Dim fileName As String = Path.GetTempFileName() WriteToFile(fileName, encrypted) Return fileName End Function Public Shared Sub WriteToFile(ByVal fileName As String, _ ByVal encrypted As String) Dim writer As StreamWriter = New StreamWriter(fileName, False, _ Text.UTF8Encoding.UTF8) writer.Write(encrypted) writer.Close() End Sub Public Shared Function ReadFromFile(ByVal fileName _ As String) As String Dim reader As StreamReader = New StreamReader(fileName, _ Text.UTF8Encoding.UTF8) Dim result As String = reader.ReadToEnd() reader.Close() Return result End Function
The example in Listing 4 overloads WriteToFile to permit using a temporary file name or providing an explicit file name.
Decrypting and Restoring the DataSet
When you read the encrypted file, you need to call the Decrypt method to return the XML string to its unencrypted value. To restore the dataset, you need to call DataSet.ReadXML to restore the unencrypted XML string to a DataSet and then perform an update operation against the database. Listing 5 shows how easy it is to reconstitute the dataset from the decrypted XML string.
Listing 5: Restoring a DataSet from an XML string
Public Shared Function Deserialize(ByVal XmlData As String) As DataSet Dim stream As MemoryStream = _ New MemoryStream(Text.UTF8Encoding.UTF8.GetBytes(XmlData)) Dim data As DataSet = New DataSet data.ReadXml(stream, XmlReadMode.InferSchema) Return data End Function
Encrypting Offline Data, .NET Style
The .NET framework helps you work at a higher level of abstraction by encapsulating a lot of complex subjects into higher-level constructs. However, a lot of legwork still has to be done for useful tasks—such as encrypting offline data.
This article offered you an opportunity to experiment with ADO.NET, XML serialization, streams, and the DPAPI. You can reuse the DPAPI any time you need to encrypt user data.
About the Author
Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Look for his upcoming book, UML DeMystified, from McGraw-Hill/Osborne (Spring 2005). Paul is also the founder and chief architect for Software Conceptions, Inc. founded 1990. He is available to help design and build software worldwide. You may contact him for consulting opportunities or technology questions at email@example.com.
If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org.
Copyright © 2004 by Paul Kimmel. All Rights Reserved.