|
As readers of this blog, you know that I'm a big fan of making things thin
and light-weight, if at all possible. Is there a reason that should not work
for XML Signatures?
Fact is, lots of applications require XML content to be encrypted and/or signed. There
is an extensive standard
called XML DSig that defines how to do that. Except for the "minor" problem that just about
everybody seems to agree (example)
that it is unworkable in many, if not most circumstances. Even most of the implementations
listed on the XML Dsig page
seem to be defunct.
So what is one to do? Nobody seems to have much in terms of constructive suggestions,
except what the Jabber folks have
been doing: creating their own way of doing it which is much simpler for the application
they are looking at. Its essence is that they take
certain DOM Nodes from the XML file, encrypt or sign them as
blobs, and then
re-insert the result into the XML file as CDATA sections.
This is something simple that works, but it has one main problem: XPath expressions
and pretty much all XML machinery breaks at the boundary between signed and unsigned
content in the same file. So how could one fix this?
Let's use a simple example. Let's say I'm creating an RSS (or Atom) feed, and I'd like
downstream syndicators and remixers to be able to "proof" to their
subscribers that my entries indeed came from me without going back to my blog and
checking (after all, I might have changed them in the meantime).
That would be a simple, but useful example, right?
To shows this, here is a basic RSS example file. Assume that I want to digitally sign the
lone item in this file.
<rss>
<channel>
<title>Johannes Ernst's Blog</title>
<language>en</language>
<item>
<title>So what about really simple XML Signatures?</title>
<description>
As readers of this blog, you know that I'm a big fan of making things thin
and light-weight, if at all possible.
</description>
</item>
</channel>
</rss>
The following example shows how the Jabber folks would probably go about it. (If I'm wrong,
could somebody please correct me?)
<rss>
<channel>
<title>Johannes Ernst's Blog</title>
<language>en</language>
<item>
<e2e>
<![CDATA[
Content-Type: multipart/signed; boundary=next;
micalg=sha1;
protocol=application/pkcs7-signature
--next
Content-type: application/xml
<title>So what about really simple XML Signatures?</title>
<description>
As readers of this blog, you know that I'm a big fan of making things thin
and light-weight, if at all possible.
</description>
--next
Content-Type: application/pkcs7-signature
Content-Disposition: attachment;handling=required; filename=smime.p7s
[actual signature data goes here, suitably encoded]
--next--
]]>
</e2e>
</item>
</channel>
</rss>
I'm making up a few things here because this isn't about Jabber, but I think this is
the gist of how one would apply what they developed for the more generic case.
Now here comes my proposal, which, if anything, is even simpler. Because of that,
I've decided to call it XML-RSig (for Really simple Signature). The gist of
the XML-RSig proposal is to leave the XML intact (so that all the XPath expressions
still work) but to add the signature into a separate DOM Node.
Also, I'm proposing
to use LID to retrieve public
keys from the source URL, because there is an existing spec for that, a bunch
of implementations (including
from NetMesh) and it fits
into the larger identity picture. Putting these together, and assuming I was the sender,
it would look as follows:
<rss>
<channel>
<title>Johannes Ernst's Blog</title>
<language>en</language>
<item>
<title>So what about really simple XML Signatures?</title>
<description>
As readers of this blog, you know that I'm a big fan of making things thin
and light-weight, if at all possible.
</description>
<rsig:signature xmlns:rsig="tbd"
lid="http://netmesh.info/jernst"
lid-credtype="gpg --clearsign">
[actual signature data goes here, suitably encoded]
</rsig:signature>
</item>
</channel>
</rss>
Here is the algorithm to construct the signature:
- Pick a node, any node ;-), to sign. In this case, we picked the
<item> node.
- "Cut out" this node, from the first character of the start tag through, and
including the last character of the end tag. In this case, this would be the section
<item>...</item>. Consider this a separate file.
- Apply the signature algorithm to that separate file, as you would for any blob. To
convert characters to bytes, apply the character set of the overall XML file.
In my example, I used
gpg --clearsign (an enumerated value defined
in LID — a fancy way of
saying "create a gpg signature"). The private key is maintained by me,
the public key can be retrieved from the URL given in the lid attribute
as specified in the LID spec.
- Re-insert the cut-out file exactly where you took it from, so the file looks the
same as before.
- Insert a new node into the XML file by character insertion. This new node contains the
signature. In my example, I called that node
<rsig:signature>. This
node becomes a child of the node whose signature it is.
To validate a signature, perform the same algorithm (after you removed all traces
of the <rsig:signature> nodes, taking care you don't accidentally
add or remove white space and the like) and compare the results.
Here are the pros and cons of this proposal as I see them:
- It's dead-simple.
- There are no normalization rules and what have you.
- It works with pretty much all signature algorithms. I chose GPG in this example because
it is broadly available and simple to use.
- For many cases, it might be possible to implement this even without bothering with
an XML parser! (if you have a good idea about what XML features will be used in
the XML you are signing, which is probably true for many generated XML files
that are signed at the point of creation)
- All XPath expressions remain intact, and consumers of the file who are not aware of
or not interested in signatures can simply ignore them.
- It seems rather general-purpose.
- It can only sign one node (and all its children) at a time.
- It works on blobs, rather than on the level of XML. That has good and bad
consequences. On the good side: simplicity.
For encryption, a very similar algorithm can be employed. The only difference: instead
of re-inserting the document in step 4, we insert a new node, whose content is the
encrypted sub-tree, such as:
<rss>
<channel>
<title>Johannes Ernst's Blog</title>
<language>en</language>
<rsig:encrypted xmlns:rsig="tbd"
lid="http://netmesh.info/jernst"
lid-credtype="gpg --clearsign">
[actual encrypted data goes here, suitably encoded]
</rsig:encrypted>
</channel>
</rss>
So much for this proposal. Now it's time for you to shoot and
tell me why it won't work ;-)
|