.NET’s tracing infrastructure isn’t perfect, but it gives you access to some ‘internal’ things that it is otherwise hard to get access to (like network and WCF tracing). Sometimes the things you want to trace are sensitive in nature, and probably shouldn’t be left lying around on the file system, even on your servers. Additionally you don’t want to set up a centralised, secure, logging system, and don’t want the overhead of more network traffic for every trace write (which can be pretty verbose sometimes). So what to do?
I was faced with these challenges recently on a project with my friends from Mexia, and one of the ideas we kicked around was using PKI to encrypt the trace. The app.config file for your .net process has the public key in it, and the app can write trace information, but not read the trace files it has created. Although we didn’t end up going with this approach, I thought it would be interesting to spike out an encrypted XML trace listener that can be dropped in in place of the regular XML trace listener, for things like WCF tracing.
Configuration
The configuration is hopefully what you’d expect. The base64-encodeed key and exponent for the RSA crypto provider are stored in a delimited string in the .config file, along with the directory you want to write the files to (even though the name says EncryptedXmlWriterTraceListener it would be more accurate to call it the EncryptedFileXmlWriterTraceListener, but the base class the XmlWriterTraceListener is fairly file-centric too).
<add name="EncryptedListener" type="JCooney.Net.Diagnostics.EncryptedXmlWriterTraceListener, JCooney.Net.Diagnostics" initializeData="Key=pUY9KVcmHF1yvM7YkdvJFMWmQkayeqqYh37kuSQjeYdx1gRtD3dZDF37dd/qbnSujwIX0ebPc6FYDqPCnpjlWA6WqLGvBRvfr2iIBuImkBYKuDP5L2Hun3U97fsz86HTMyf1zo9+WvFQifeDsJtsGuL6RLEOiYoBcla/FTC3fGk=;Exp=AQAB;Directory=f:\temp\" />
Asymmetric Keys, to Encrypt Symmetric Ones
Public key cryptography is not great for encrypting large chunks of data (to do so you generally need a key that is bigger in size than the data you want to encrypt). On the web for instance the public key infrastructure is used so that two parties can exchange a shared symmetric key, which is then used for the duration of their secure conversation. Here I do a similar thing, where the first portion of the file is actually a new symmetric key and IV for the Rijndael cipher that has been encrypted with the public key stored in the config file. To decrypt the log file you use your private key to decrypt the symmetric key and IV, which you then use to decrypt the bulk of the file using the Rijndael cipher. I’ve rolled up this process into a helper class called EncryptedXmlWriterDecrypter which takes a file stream (the encrypted file) and the private key information. Generating new keys is done by creating a new instance of the RSACryptoServiceProvider and serializing out the key using ToXmlString.
Known Issues
I have seen one issue where the decrypted file is incomplete and the closing E2ETraceEvent element has not been written to disk before the trace file is closed, or not read back properly when it is decrypted. I wasn’t easily able to determine the cause of this.
Cryptography Is Hard
This brings me to my last point – cryptography is hard, I’m certainly not an expert at it, and this is very much a proof of concept at this stage. If anyone has any feedback, or pull requests I’d love to see them, but please subject this to some thorough testing before putting it out in production.