XML Encryption & Decryption in VB.NET

Posted by Beau on Monday, August 16th, 2010 at 5:23 pm

One of my professors at Purdue always raved about XML and how it was a powerful language. I thought it was a cool language, sure, because you could make up your own tags, but the ways I could use it escaped me at the time. However, I’ve begun using XML as a database, and it coupled with XPATH have opened to my eyes to a dynamic data storage world. It makes it easy to create and read data, but it also makes it easy to share between applications. Up until this “aha!” moment, I found XML only to be useful for RSS feeds and weather data.

But, the easy readability of XML comes at a price: if you’re storing sensitive data, any person who can read has a pretty good shot at understanding it. So, I went on a quest to find out ways to password-protect or encrypt XML files, and found out that recently the .NET framework began supporting a few easy to use methods to make encryption/decryption a fairly simple task. I’ll cover both of these, with some caveats, in Visual Basic .NET, since that’s the language I’ve been writing in.

I’ll make the assumption that if you’re writing this code, you’re probably using one of the iterations of Visual Studio, since it’s pretty great for writing anything .NET related. I’ll also assume that you know how to create and understand XML files, or at the very least have a sample file you’re willing to use, and that you have basic knowledge of programming in VB. Of course, questions are always welcome!

Before you do anything in your project, you’ll want to make sure you add references to the Security classes in the framework by right clicking on the project’s name and clicking on “Add Reference” which should make a window pop up with a massive list of different libraries. Scroll down to System.Security, click on it, and then click the Add button. You might be able to get away with skipping this step, but any time I’ve needed to use the XML Security libraries, Visual Studio has had trouble finding the methods I was trying to use, and adding the reference has always solved those problems.

First, you’ll want to import the appropriate things for dealing with the encryption:

Imports System.Xml
Imports System.Security.Cryptography
Imports System.Security.Cryptography.Xml

The code for encryption is below. I like to stick it in a “Save Button” or similar subroutine, where it copies whatever XML document I’m working with and saves it to the encrypted file.

'Declare an object that will load the XML file
        Dim xmldoc As New XmlDocument()

        'Always use the TRY/CATCH block to catch exceptions
        Try
            'Load the XML file
            '  Note that it says "decrypted.xml" (we're either beginning with a new document, or one that has been
'   decrypted when the program loaded so we could work with it)
            xmldoc.Load("C:\Temp\decrypted.xml")

            'Create a shared key using the triple DES algorithm
            '  Useful if we want to use this XML file across different applications
            Dim sharedkey As New TripleDESCryptoServiceProvider()
            'Declare a writer to save the key to a file (I use C:\Temp\ for my directory of choice
            Dim writer As IO.StreamWriter = New IO.StreamWriter("C:\Temp\sharedsampleKey.txt")
            'Convert the key to a String
            Dim str As String = Convert.ToBase64String(sharedkey.Key)
            'Write the string to the file
            writer.WriteLine(str)
            'Close the writer since we're done with it
            writer.Close()

            'Create an encrypted XML object and give the constructor our loaded XML file
            Dim exml As EncryptedXml = New EncryptedXml(xmldoc)

            'Select the XML node/element to be encrpyted
            '  You can select single nodes to encrypt, or choose the root node to encrypt the entire document
            Dim encryptElement As XmlElement = CType(xmldoc.SelectSingleNode("/cookbook"), XmlElement)

            'Encrypt the XML element data using the TripleDES alogrithm and save the results into a byte array
            Dim encryptXML As Byte() = exml.EncryptData(encryptElement, sharedkey, False)

            ' Create an EncryptedData object and tell it how we're encrypting it
            Dim ed As New EncryptedData()
            ed.Type = EncryptedXml.XmlEncElementUrl
            ed.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncTripleDESUrl)
            ' Create a CipherData element
            ed.CipherData = New CipherData()
            'Tell the cipher what we're encrypting
            ed.CipherData.CipherValue = encryptXML
            'Replace the node with the encrypted content
            EncryptedXml.ReplaceElement(encryptElement, ed, False)

            'Save the encrypted version of XML to disk
            xmldoc.Save("C:\Temp\encrypted.xml")

            deleteLbl.Visible = False
            messageLbl.Visible = True
            messageLbl.Text = "Recipe Data Saved"
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try

In the above code, I alluded to decrypting the file so that it’s easier to work with in your program, and if you plan on encrypting data, it doesn’t do you much good if you can’t go back and read it later. Here’s how the decryption process works, if we assume that you used the method above to encrypt your XML in the first place. I like to put this in the Form’s Load subroutine, or you could put it in a “Load File” button, for example.

 Try
            'create a shared key object that we'll use to "unlock" our data
            Dim sharedkey As New TripleDESCryptoServiceProvider()

            'Retrieve shared key from the stored location
            Dim rd As IO.StreamReader = New IO.StreamReader("C:\Temp\sharedsampleKey.txt")
            'read the string fromt he file and convert it to a byte array
            Dim bytedata() As Byte = Convert.FromBase64String(rd.ReadToEnd())
            'set the key in the object
            sharedkey.Key = bytedata

            'Declare an XML object and load our encrypted content
            Dim encryptedDoc As New XmlDocument()
            encryptedDoc.Load("C:\Temp\encrypted.xml")

            'Grab whatever it is that we've encrypted
            Dim EncryptedElement As XmlElement = CType(encryptedDoc.GetElementsByTagName("EncryptedData")(0), XmlElement)

            'Create an EncryptedData object
            Dim ed As New EncryptedData()
            'Load the encrypted node into our ED object
            ed.LoadXml(EncryptedElement)

            'Use the key and the DecryptData method to get the original text
            Dim encryptXML As New EncryptedXml()
            Dim decryptedXML As Byte() = encryptXML.DecryptData(ed, sharedkey)

            'Replace the encryped node with the decrypted one
            encryptXML.ReplaceData(EncryptedElement, decryptedXML)
            'Save the data to a decrypted file
            encryptedDoc.Save("C:\Temp\decrypted.xml")
            'close our stream reader
            rd.Close()
        Catch ex As Exception
            Messag

Caveats

The above methods are really only for demo purposes, but in production, you’ll want to be a little more careful with your data. For example, using the above, you’ll take an encrypted file, decrypt it, and save it to the hard disk. Unless you delete it, the data will be there for just anyone to read. That being said, you should delete it once you’re done working with it (obviously), by using something like the below:

Private Sub frmMain_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
        Dim FileToDelete As String

        FileToDelete = "C:\Temp\decrypted.xml"

        If System.IO.File.Exists(FileToDelete) = True Then
            System.IO.File.Delete(FileToDelete)
        End If

    End Sub

Or, conversely, you could stick to using one file and just overwrite the encrypted file’s XML with the decrypted XML using the methods above, but you’d have to make sure you encrypt it again when you’re done with it.

Then, there’s the matter of the key. It’s just sitting there next to the data, but when you hide a spare key in your backyard, you probably don’t leave it taped to your door. Anyone with sense could break in just as easily if you’d left it unlocked. So, that said, you want to keep your shared key in a less-than-obvious place. And then, you may want to pad your key with a few hundred extra characters just to throw off anyone who finds it. I’ll probably write another post soon describing what I mean, but for now, it’s back to work for me!

Further Reading

Tags: , , , ,

This entry was posted on Monday, August 16th, 2010 at 5:23 pm and is filed under Visual Basic.NET. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

Leave a Reply