Application developers' choices for storing data on the local machine were limited for a long time — you either used a database or stored the data in flat files. With the advent of XML and the .NET Framework, the possibilities have multiplied.
.NET developers are familiar with using serialized DataSets to store local application data; this technique is used extensively in the WinForms development arena. The problems with this method are that anyone with access to the file can easily read the data and possibly even write to the file. The application would pick up the modified data and treat it as valid.
These issues caused me to see how I could secure local data held in serialized DataSets. There are many options for securing the data — from encrypting each field to hashing the data and comparing hashes when the data is deserialized. The most direct approach is to encrypt or decrypt the serialized DataSet before writing to it or after reading it from disk. This method provides a suitable amount of security and protects against anyone tampering with the file.
Serializing and encrypting the DataSetThe first step to encrypting the DataSet is to serialize it in memory. To do this, I will use the XmlSerializer object to serialize the DataSet into a StringWriter object. The StringWriter object allows you to treat a StringBuilder as if it was a normal Stream. This is what gives you the ability to serialize the DataSet in memory without writing it to disk. The code for this is shown in Figure A. Figure A
In the code, there's a call to the EncryptString function after the DataSet is serialized. This function is a wrapper around the DES encryption mechanisms that are included with .NET. EncryptString takes a plain-text string of data (along with a key value) and returns a string that has been DES encrypted using the supplied key value. The code for EncryptString is in Figure B. Figure B
After the EncryptString function returns the encrypted data, the StreamWriter object is used to write the data to disk. At this point, the data is securely on the local disk and ready to be decrypted and read. Since the data is encrypted, anyone opening the file will see a jumble of characters and will not be able to get information out of the file or write usable information to the file.
Deserializing and decrypting the DataSetReading the encrypted DataSet is just as easy as writing it, except it is done in the opposite order. First, the encrypted data is read from disk using the StreamReader object as shown in Figure C. Figure C
After the data is read, DecryptString is called. This function decrypts the data sent into it using the "key" parameter as the decryption key. If the key that is sent into DecryptString does not match the key that was used when EncryptString was called, the data will successfully decrypt. Figure D shows the code for DecryptString. Figure D
After the data is decrypted, an XmlSerializer object is used to convert the raw data back into a usable DataSet. Note that the WriteEncryptedDataSet and ReadEncryptedDataSet functions are completely generic and do not rely on any specific schema of the DataSet. This allows you to use the same functions to read or write any DataSet in a secure manner.