Earlier this week, I posted about using cipher references to refer to data stored in the same document. Today I'll use the same technique, but instead of storing the encrypted data elsewhere in the document, I'm going to store it on a seperate server. Of course, I'll be using the familiar order.xml sample to work with. The samples all assume that I have a webserver with a shared root folder on \\webserver\wwwroot, which is accessed at http://webserver.

Encrypting the data

Encrypting the data to store on a server is very similar to the encryption step for storing locally. However, since my data will be in a seperate file, and not embedded in XML, I've decided to not base64 encode it. Instead, I'll just write the bytes out to a binary file.

// load the data
XmlDocument doc = new XmlDocument();
doc.Load("order.xml");
XmlElement paymentElem = doc.SelectSingleNode("/order/payment") as XmlElement;

// encrypt
SymmetricAlgorithm key = new RijndaelManaged();
byte[] cryptData = new EncryptedXml().EncryptData(Encoding.UTF8.GetBytes(paymentElem.OuterXml), key);

// store the encrypted data on a web server
using(FileStream writer = new FileStream(@"\\webserver\wwwroot\orders\order.bin", FileMode.Create))
{
    writer.Write(cryptData, 0, cryptData.Length);
    writer.Close();
}

Creating the EncryptedData

Creating a cipher reference for this data will be much easier than it was when I was referencing the local data. Since the local data was base64 encoded, and stored within an XML element, I needed to apply a transform chain. However, the data this time is stored as raw bytes, so no transforms are needed. This means I can just use the CipherReference constructor to point to the URI where the data is stored, without having to hand-craft the CipherReference XML myself. Once I have the CipherReference, setting up the EncryptedData object is relativly straight forward, and very similar to the standard XML encryption case.

// create a cipher reference to the stored data, and setup the encrypted data
CipherReference cr = new CipherReference("http://webserver/orders/order.bin");
EncryptedData encrypted = new EncryptedData();
encrypted.CipherData = new CipherData(cr);
encrypted.KeyInfo.AddClause(new KeyInfoName("key"));
encrypted.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
EncryptedXml.ReplaceElement(paymentElem, encrypted, false);

This creates the following XML:

<order>
  <purchase>
    <item quantity="1">Def Leppard: Pyromania</item>
    <item quantity="1">Ozzy Osbourne: Goodbye to Romance</item>
  </purchase>
  <shipping>
    <to>Shawn Farkas</to>
    <street>5 21st Street</street>
    <city>Seattle</city>
    <state>WA</state>
    <zip>98000</zip>
  </shipping>
  <EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#">
    <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <KeyName>key</KeyName>
    </KeyInfo>
    <CipherData>
      <CipherReference URI="http://webserver/orders/order.bin" />
    </CipherData>
  </EncryptedData>
</order>

Decrypting

Decrypting this document works almost exactly the same as with standard XML encryption, however there is one difference. Since the XML decryption engine will have to go to an arbitrary website and download some data, this could expose a security hole (depending on how much you trust the XML document). In order to establish the trust-level for the specific document, there is a property, DocumentEvidence, on the EncryptedXml class which represents the evidence for this document. It is up to you to set this property properly. For instance, if the document came from the internet, you might add Zone evidence for the Internet zone, and perhaps some site evidence for the site it came from. Under the default security policy, that would restrict the document from getting data from any website but the same one it came from. In this instance, since http://webserver looks like an intranet site, I'm going to setup LocalIntranet and Site evidence.
EncryptedXml decryptXml = new EncryptedXml(doc);
decryptXml.DocumentEvidence = new Evidence();
decryptXml.DocumentEvidence.AddHost(new Zone(SecurityZone.Intranet));
decryptXml.DocumentEvidence.AddHost(new Site("webserver"));
decryptXml.AddKeyNameMapping("key", key);
decryptXml.DecryptDocument();

Once again, after this is run, the XML document contained in the doc variable is exactly the same as the original order XML.