Click here to Skip to main content
Email Password   helpLost your password?

Introduction

XML is an easy, portable way to save DataSets within the .NET Framework. All methods to handle the XML are contained within .NET, so the end user will not need proprietary data handling software or server to read in the data.

I recently needed the ability to save encrypted DataSets to track a student's learning progress. Obviously, the XML format was too accessible to save this sort of information which needed to be kept from tampering, so I needed encryption, which comes standard with .NET. However, I was still facing a few challenges:

  1. I wanted to be able to accomplish all encryption and decryption with just a few calls to a special "crypto" class library.
  2. While decrypting a file in .NET is very easy, I wanted to avoid having temporary unencrypted files written to disk in the process.
  3. The start of every XML file is quite standard, so it can potentially be used to "guess" the encryption key and crack the code.
  4. Finally, I needed a mechanism to recognize encrypted files so the DataSet would not load erroneous data.

I set out to develop a class library (XMLEncryptor) to encrypt and decrypt DataSets using a username and password. The challenges above were handled as follows:

The XMLEncryptor class is instantiated using two strings, a user name and password. The constructor creates the encryption key and initialization vector, as well as a 16-byte signature.

The signature, which is written as a file header without encryption, is used by the XMLEncryptor to identify files as having been encrypted using its methods. Also, the signature serves as a mask for to push the standard XML header to the 17th byte in the file, which should offer some level of protection against "guessing" the XML header (see 3 above).

The XMLEncryptor exposes two public functions, one for reading in the encrypted XML file and one for writing the DataSet back to a file:

The ReadEncryptedXML function will return a DataSet if all goes as intended. The encrypted XML data is decrypted into a MemoryStream, which is subsequently used to load a DataSet which can then be returned to the caller without having intermediary files written to disk. If an error is encountered during reading, decryption etc., the ReadEncryptedXML function returns null.

The WriteEncryptedXML function takes the DataSet and writes it to a file using the same encryption key and IV as was used for the decryption. Once again, no temporary files are involved.

A typical use of the XMLEncryptor would be as follows:

{
   XMLEncryptor XMLenc = new XMLEncryptor("myname", "mypassword");
   // Load encrypted file into DataSet

   DataSet myDataSet = XMLenc.ReadEncryptedXML("myfile.enc");
   // test for successful read

   if (myDataSet == null)
   {
      // Do something

      return;
   }
   // write back the dataset

   XMLEnc.WriteEncryptedXML(myDataSet, "myfile.enc");
}

Limitations of the application are:

  1. The handling of the username and password is left up to the parent application.
  2. Files of more than 2 GB cannot be handled. However, since the process involves in-memory handling of data, practical size limits may be smaller.

That's all!

Enjoy.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralMore elegant way
super-e-
0:00 21 Sep '09  
I think a much easier and elegant way to handle this is to use the WriteXml and ReadXml methods of the dataset class, passing in as an argument a CryptoStream object (System.Security.Cryptography namespace). CryptoStream derives from Stream class so, the process is straightforward and pratically transparent to the dataset class.

source would look like:

        public void Encrypt(DataSet ds, string filename)
{
outFile = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write);
encryptStream = new CryptoStream(outFile, encryptor, CryptoStreamMode.Write);
ds.WriteXml(encryptStream);
encryptStream.Close();
outFile.Close();

}

public DataSet Decrypt(string filename)
{
DataSet result = new DataSet();
inFile = new FileStream(filename, FileMode.Open, FileAccess.Read);
decryptStream = new CryptoStream(inFile, decryptor, CryptoStreamMode.Read);
result.ReadXml(decryptStream);
decryptStream.Close();
inFile.Close();
return result;
}


encrypt and decryptor objects are defined as:
        ICryptoTransform encryptor;
ICryptoTransform decryptor;
and can be obtained in different ways, depending on the programmer's choices. I suggest the fully managed RijndaelManaged.CreateEncryptor() and RijndaelManaged.CreateDecryptor() methods for symmetric encryption.

Hope this helps someone.
Generalbug fix
Ronchen777
5:28 7 Jul '09  
Great work! I've noticed only one problem with ReadEncryptedXML() - if you specify afile located elsewhere on the machine, it will fail. You should change this line:

inFile = new FileStream(fi.Name, FileMode.Open);

with the following

inFile = new FileStream(fi.FullName, FileMode.Open);

Thanks again
GeneralMade one change in XmlEncryptor
shedao
11:08 21 Oct '08  
I had to change line #173

//from:

inFile = new FileStream(fi.Name, FileMode.Open);

//to:

inFile = new FileStream(fi.FullName, FileMode.Open);


I needed to pass in the full path otherwise it would default to the application path (bin directory)


otherwise works great! thanks for doing the heavy lifting
NewsTwo other related encryption articles in CodeProject ...
Tony Selke
7:49 27 Sep '07  
You may also be interested in looking at the following, related Code Project articles:

Generic SymmetricAlgorithm Helper[^]
This is a generic helper class that exposes simplified Encrypt and Decrypt functionality for strings, byte arrays and streams for any SymmetricAlgorithm derivative (DES, RC2, Rijndael, TripleDES, etc.).

Making TripleDES Simple in VB.NET and C#[^]
This is a simple wrapper class that provides an easy interface for encrypting and decrypting byte arrays and strings using the 3DES algorithm.

GeneralCannot decrypt in VB.Net
stmarcus
20:24 28 Dec '06  
Hi,

I am using your code in a VB.NET project. I included your DLL. The code encrypts the file very well (apparently), as the file is the proper size. But it will not decrypt. I get Nothing as my data set.

Can I use any user name and password, or are there limitations?

Any other ideas as to how to debug the problem?

Thank you!
AnswerRe: Cannot decrypt in VB.Net
stmarcus
9:19 29 Dec '06  

I found the first problem. In your source file at line number 171:

try {
inFile = new FileStream(fi.Name, FileMode.Open);
}
catch (Exception exc)

should be:

try {
inFile = new FileStream(fileName, FileMode.Open);
}
catch (Exception exc)

... otherwise, the routine can only fnd files in the current windows diretory. "fi.name" is just a file name, and ignores the path passed in by the user.

Now that I am beyond this minor glitch, the routine does not successfully deserialize. It produces a slightly corrupt data set. I am using an xsd file to determine column types, and those types are off. Also, some columns are missing.

Do you have rules for the user name and password? The length? The characters available for those two strings? Any other reason for this sort of problem?

Thank you!
QuestionData Type in the Decrypted File
Luis Mora
15:27 16 Dec '06  
Hello,

Another problem I found is all fields in the xml decrypted file are type="xs:string" it won't keep original xml schema.

Is there a workaround to keep original schema.

Thank you in advance

Luis Mora

AnswerRe: Data Type in the Decrypted File
Koenraad
12:38 8 Jan '07  
Hi Luis,

This is entirely dependent on the XSD or dataset that was read in in the first place. The encryption/decryption is done at the file level, not at the schema/XML level. If your xsd schema contained int16 etc, it should come out normally.

If you have a sample xsd you want to share, I can test it out.

Koenraad
GeneralVB.NET and the null result
Luis Mora
14:09 16 Dec '06  
Hello,

I referenced your code in one of my projects and it worked just fine. Thank you very much !! It saved a lot of work.

I just have a little problem once I read an encrypted file using your function I really don't know how validate the null response in VB.net 2003 when it wasn't read properly

For example

Dim decryptor As New CryptoXML.XMLEncryptor("user", "password")

ds = decryptor.ReadEncryptedXML(ofdlg.FileName)

Here I don't know how to validate a null response as posted in this article.


Can you help ?

Luis






Luis Mora

GeneralRe: VB.NET and the null result
Koenraad
12:34 8 Jan '07  
Hi Luis,

Sorry for the delay in getting back; I'm switching jobs & moving across the Atlantic, there has been little time for hobby programming!

Glad you found the code useful. I was a bit rusty on the details but here we go again:

In the ReadEncryptedXML() funton, you will find several checks, but they are mostly very basic (like passwords that are too small, file that don't exist, verifying the signature, etc). There is of course always the exception handler.

The check on whether a DataSet is valid, is entirely dependent on the code in lines 200-210:

// Read in the DataSet from the CryptoStream
DataSet data = new DataSet();
try
{
data.ReadXml(csDecrypt, XmlReadMode.Auto);
}
catch (Exception exc)
{
Trace.WriteLine(exc.Message, "Error decrypting XML");
return null;
}

One can dump additional data from the Exception, but that's probably as far as it gets. It's essentially the same mechanism that .NET uses when you load an unencrypted dataset, not more, no less.

Hope this helps
Koenraad




Generalbetter ways
ediazc
9:14 3 Aug '05  
I think is not always necesary to encrypt.
I Propose a digital signature method for prevent tampering, in my recent article: http://www.codeproject.com/useritems/ProtectYourXmlData.asp[^]

Eduardo Diaz


Dark Side Programming"
Generalencryption explanation
Ashley van Gerven
22:28 24 Apr '05  
perhaps you could outline more how the encryptiong / decryption takes place? (esp. since that is the main feature of the code)
GeneralRe: encryption explanation
Koenraad Blot
2:02 25 Apr '05  
The encryption/decryption starts by creating an algorithm, I used a symmetric algorithm called RijndaelManaged.

>> RijndaelManaged rijn = new RijndaelManaged();

The algorithm then creates a decryptor, using the key and IV byte[] arrays (these byte[] arrays are created during the construction of the CryptoXML object, see GenerateKey() and GenerateIV() functions).

>> ICryptoTransform decryptor = rijn.CreateDecryptor(md5Key, md5IV);

Following this, there is a sequence of events to manipulate the encrypted data.
1. I read the encrypted file into a byte[] buffer.
2. From the byte[] buffer, I can subsequently create a MemoryStream object.
3. The MemoryStream is then read by the decryptor into a CryptoStream object
4. The CryptoStream object, which now contains decrypted data, can be read by a newly created DataSet to read in the XML part of the data.

The code is somewhat compplicated by the fact that the unencrypted signature header needs to be handled as well.


GeneralDataset Encryption
Dale Sides
15:53 24 Apr '05  
From quickly browsing the article and not trying the code yet it appears that you could take a dataset generated from any datasource, not just XML, to create the encrypted XML dataset. Am I correct in this assumption?
GeneralRe: Dataset Encryption
Koenraad
2:07 25 Apr '05  
Potentially, yes. But the ReadXML method is the only DataSet method that reads files/streams into the DataSet. I never had to work with servers so I would not know how to get there. The code in my article would probably need extensive change to deal with this possibility.


Last Updated 23 Apr 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010