Introduction
Recently one of our costumers wanted us to protect a log file from being tampered. The requirement was to maintain a log file of the main activities done by the user inside the system. For different reasons, no database was allowed, so we had to make this log using plain files. Some of the fields needed to be encrypted, and a check mechanism was required to assure that no data was altered.
Well, we built our application and mapped the log file to an XML DataSet. We defined an XML Schema File (XDS) for the log file. This allowed us to use a Typed DataSet, and the program worked as if a database existed. .NET Typed DataSets in conjunction with XML and XSD are a good combination. For example, with these we could use our Grid components.
Integrity of data
This article shows how we solved the anti tampering of the log file. We have XML data, an XSD and classes in C# generated by Visual Studio from XSD. These classes allowed us to read/write the log file. For integrity check, we decided to use digital signatures. The standards for digital signatures are defined by W3C and IETF, you can read all about this standard in this link.
A class for Xml-DSig
XmlSigner
is a class that implements two methods:
public static void SignXml (string fileName);
public static bool CheckSignXml (string fileName)
Both the methods receive a fileName
. SignXml
aggregates a Digital Signature Block at the bottom of the file, this block element is called Signature. CheckSignXml
looks into the file specified and returns true
if the signature is correct. You can download the full implementation and adapt it for your use.
Key management
We use public
-private
key pairs for signature generation. In this class, the signature is stored in the machine storage. This is done by the private
internal method GenCsp
. The code for the method is given below:
private static RSACryptoServiceProvider GenCsp()
{
CspParameters cp = new CspParameters();
cp.KeyContainerName = keyStorage;
cp.Flags = CspProviderFlags.UseMachineKeyStore;
cp.KeyNumber = 2;
return new RSACryptoServiceProvider(cp);
}
keyStorage
is a private string variable that defines the name of the storage for the keys, it's accessible through a static
property named KeyStorage
. This code assumes that the check will be made in the same machine over and over again. If you want to use other storages you can export the public
key and sign with this, and check with the private
key, perhaps in a future modification I will show you how to do this.
Usage
We use the XmlSigner
class with our Typed Dataset in this way:
MyTypedDataSet myDataset;
void SaveData()
{
myDataset.WriteXml(logFileName);
XmlSigner.SignXml(logFileName);
}
void LoadData()
{
if (XmlSigner.CheckSignXml(logFileName))
throw new Exception("Bad Digital Signature,
file was tampered");
myDataset.ReadXml(logFileName);
}
Problems
This class suffers from the following problems:
- Uses files on disk, so somebody can take the files before signing (in a very paranoiac scenario this can be done using
System.IO.FileSystemWatcher
.
- The keys are stored in the machine storage, suppose you want access the file across a LAN, you need an outside key, perhaps in a smartcard or a certificate.
Extensions
I will extend the class to sign XmlDocuments and DataSet
s.
Encrypting XML elements
This article is a good starting point; in fact this article inspired and guided me to cryptography with C#. Using the class EncDec
, described in that article, you can make cryptography of elements. Remember, we where signing a Typed DataSet. In this case a Typed DataRow is generated from the original XSD, so an alternative is to modify the gets and sets of the Typed DataRow fields. Of course, if you modify your XSD, then you could lose your code. The code here shows how I solved changing my get
and set
of the fields I wanted to encrypt:
public string Id {
get {
try {
return EncDec.Decrypt(
((string)(this[this.tableMyTable.IdColumn]),
"my password");
}
catch (InvalidCastException e) {
throw new StrongTypingException("Cannot get " +
"value because it is DBNull.", e);
}
}
set {
this[this.tableVerificacion.IdColumn] =
EncDec.Encrypt(((string)(this[this.tableMyTable.IdColumn]),
"my password");
}
}
If you are generating your data via XmlSerialization, you can use the same approach. If you are using DOM, then it's more difficult, but feasible.
Other encryption tools
Here, you can find the XML Security API, in both C++ and Java. I will write a wrapper for use in C# shortly, so stay tuned....
Conclusion
The Signature Element added to the data doesn't interfere with its content, and all the mechanism of Typed Dataset works without any problems. When sensitive data is stored in a file, and you don't want this data to be altered the best alternative is to use Digital Signatures. In this article we used XML Digital Signatures because we needed to use XML DataSet, and in this case this was the best alternative.
Source code
If you don't want to download the code, read it and analyze it, I'll keep a printable copy in my blog.
History