Security is surely one of the most talked-about subjects this year. Many books about writing secure code have surfaced over the past several months (for Windows and Unix too!). Early in the year, Microsoft announced its Trustworthy Computing initiative. In addition, the company's partnership with IBM and Verisign has resulted in WS-Security (Web Services Security), a platform- and language-neutral security standard for Web services.
WS-Security enhances SOAP (Simple Object Access Protocol) messaging to provide confidentiality and integrity, which form the basis for higher-level security aspects, such as trust, authorization, or policy. In Part 1, I discussed XML Encryption, which covers WS-Security confidentiality. In this article, we look closer at the integrity part of the equation: XML Signature, which turns out to be a bit more complicated than its confidentiality counterpart.
Read the whole series on XML security, "Yes, You Can Secure Your Web Services Documents:"
- Part 1: XML Encryption keeps your XML documents safe and secure
- Part 2: XML Signature ensures your XML documents' integrity
A new, improved approach to digital signature
The main difference between XML Signature and other digital signature standards such as PKCS#7 (Public-Key Cryptography Standard #7) is that it is—surprise, surprise—XML-aware. That is, with XML Signature, not only can you sign arbitrary digital content as a whole, but you can also sign just specific parts of XML documents. This proves useful when parts of XML documents can be moved, copied, encrypted, signed, and verified in any order, which is quite likely to happen as we move more towards shuffling around XML messages instead of binary payload over the wire. However, as you will see later, XML awareness doesn't come free.
Compared to XML Signature, XML Encryption is relatively straightforward. You select some nodes to encrypt, and that's basically it. You decrypt the ciphertext with the correct key, and you retrieve the plaintext—as simple as that. Not so with XML Signature! XML Signature is more complicated because signatures need to be verified correctly. Now, what can be so difficult about verification? Read on.
It's the information content, not the physical representation
Generating a signature over a document usually means calculating that document's digest and encrypting it with your private key. When you need to verify it on the other side of the wire, simply recalculate the document's digest and compare it with the digest you get from decrypting the encrypted digest with the signer's public key. That way, you can ensure the document has not changed, not even slightly.
But XML Signature is XML-aware, which introduces a new challenge. Look at the snippets below:
<blah meaning="also nothing" purpose='nothing' > <blah2 /> </blah>
<blah purpose='nothing' meaning = 'also nothing'> <blah2></blah2> </blah>
Though both snippets have identical information content, because of their permissible differences, their digests will definitely differ. That's what you get when mixing XML with digital signature: digital signature checks whether a message has changed along the way, but an XML document's physical representation can—and quite likely will—change during processing, even though its information content remains the same.
Exclusive XML Canonicalization solves this problem. This W3C (World Wide Web Consortium) specification dictates how to generate the canonical form of an XML document that accounts for permissible changes such as white space or attribute ordering. As an example, if we canonicalize the two snippets above, both will yield the same canonical form:
<blah meaning="also nothing" purpose="nothing"> <blah2></blah2> </blah>
By calculating the digest over a document's canonical form, we can ensure that, as long as the document's information content stays the same, its signature will still verify, even if the physical representation has somehow changed during processing.
Yet sometimes, ordinary (also referred to as inclusive) canonicalization is not enough for XML Signature. Because of digital signature's fundamental nature, XML Signature could serve as the building block for many protocols in the future. However, in such protocols, extracting and inserting parts of XML messages into new messages is quite common. Consider the following snippet, already in canonical form:
Suppose someone signs and submits that message to the system for further processing, during which it is inserted into an envelope:
<foo:outer xmlns:foo="http://foo"> <doh:inner xmlns:doh="http://doh"></doh:inner> </foo:outer>
You get the snippet above at the other end. To verify the signature over
doh:inner, you must calculate the canonical form, which now, unfortunately, looks like the snippet below, thanks to the enveloping element's namespace:
<doh:inner xmlns:doh="http://doh" xmlns:foo="http://foo"></doh:inner>
Exclusive canonicalization handles this namespace mix-up (which will break signature verification) by excluding the envelope's context from
doh:inner's canonical form. This way, you can still verify the signature over
doh:inner even if it is removed, copied, and inserted out of and into other elements from different namespaces.
Some nodes are not meant to be decrypted
XML Signature and Encryption allow you to work on just a certain part of a document, which is good; you can sign/encrypt just the part you need while letting the system make sense of the document. This approach also works well in workflow systems, where several people may approve (by signing) or encrypt different parts of a document during its lifetime. But the fact that you can sign and encrypt a certain part of the document in any order raises an interesting problem.
Suppose your workflow engine handles rules such as "This type of transaction must be approved by a senior manager, followed by a manager." Suppose a transaction looks like the snippet below, after a senior manager named Sheridan has approved it:
<FundsTransfer> <Id>8293742</Id> <FromAccount>008-8-036982</FromAccount> <ToAccount>001-2-165921</ToAccount> <Money> <Amount>1000</Amount> <Currency>702</Currency> <Exponent>-2</Exponent> </Money> <Approvals> <Approval> <Username>sheridan</Username> <Designation>Senior Manager</Designation> <AuthCode>Gamma-six-six-Z-niner</AuthCode> </Approval> </Approvals> </FundsTransfer>
The software generates Sheridan's signature over the snippet above and then encrypts his authorization code. Next, the document goes to a manager, Ivanova. After Ivanova signs the transaction, it now looks like the snippet below. Note that her authorization code has been encrypted as well:
<FundsTransfer> <!-- omitted for brevity --> <Approvals> <Approval Id="sheridan_approval"> <Username>sheridan</Username> <Designation>Senior Manager</Designation> <EncryptedData xmlns=".../xmlenc#" Id="sheridan_authcode"> <!-- omitted for brevity --> </EncryptedData> <Signature Id="sheridan_signature"> <!-- omitted for brevity --> </Signature> </Approval> <Approval Id="ivanova_approval"> <Username>ivanova</Username> <Designation>Manager</Designation> <EncryptedData xmlns=".../xmlenc#" Id="ivanova_authcode"> <!-- omitted for brevity --> </EncryptedData> <Signature Id="ivanova_signature"> <!-- omitted for brevity --> </Signature> </Approval> </Approvals> </FundsTransfer>
At this step, the approval cycle has completed, and the document should process further. But think about it: to verify Ivanova's signature, your application must decrypt her
AuthCode field, not Sheridan's. How is your application supposed to know that it should decrypt one but not the other? That transaction only needs two approvals. What about those that need the signatures of Delenn, Marcus, and Franklin as well?
Decryption Transform for XML Signature comes to the rescue! With Decryption Transform, you can tell the recipient the proper order of decryption and signature verification; not only will signatures be verified properly, but also people don't have to sign what they can't see. That is, Sheridan can sign the document with his authorization code in the clear and then encrypt it, instead of the other way around.
The example above actually needs more than just Decryption Transform. Notice that the signatures above are enveloped signatures. That is, the signed XML element itself envelopes each signature. XML Signature also offers an enveloping signature, which envelops the signed element and a detached signature, which is completely separate from the signed data object. Now, enveloped signature is tricky, isn't it? We're supposed to calculate the signed element's digest and then put the result inside the signature. But doing so will change the
Signature element, thereby invalidating the digest! An enveloped signature transform solves this problem by excluding the
Signature element itself (along with all its descendants) from digest calculation.
Next we'll see what an XML Signature looks like.
XML Signature: The core element
Signature element represents an XML Signature and has the following structure (Note: This code is from the W3C's XML Signature page.
? denotes zero or one occurrence;
+ denotes one or more occurrences; and
* denotes zero or more occurrences.):
<Signature ID?> <SignedInfo> <CanonicalizationMethod/> <SignatureMethod/> (<Reference URI? > (<Transforms>)? <DigestMethod> <DigestValue> </Reference>)+ </SignedInfo> <SignatureValue> (<KeyInfo>)? (<Object ID?>)* </Signature>
In the structure above,
SignedInfo is a mandatory element since it is the element that is actually signed. That is, if you sign a document in XML Signature, the signature is not generated over the document's digest directly, but over the
SignedInfo element (which contains the digest).
Reference refers to the data object being signed. It includes the
Transform elements, which process in their textual order. The last transform's output becomes the input to
DigestMethod, which will produce the
DigestValue element's content.
SignatureValue is quite obvious: it contains the signature value.
KeyInfo is interesting because it is used in XML Encryption as well—to tell the processing application where it can obtain the keys to process the signatures.
Object is an optional element that can contain any data, so you can place the equivalent of PKCS#7 authenticated attributes, such as
Object also plays an important role in enveloping signatures, as it contains the signed data.
With all this talk and no coding, you're probably growing bored. From this point on, I discuss writing code for XML Signature. Shall we begin?
Which library to use?
I thought long and hard before deciding on a library for this article. In Part 1, I chose XML Security Suite from IBM mainly because of its übercool feature: the template. But I also chose it because I couldn't find an open source Java library for XML Encryption then (this is no longer true—Apache's XML Security package now supports both XML Encryption and XML Signature). The situation differs for XML Signature; many libraries are available—you can even accomplish XML Signature with J#, Microsoft's Java development tool for its .Net Framework.
At the time of this writing, two open source libraries are available: Apache's XML Security package, and Gapxse. Like IBM's XML Security Suite, Gapxse also boasts the template feature, so you don't have to study the whole API and write too much code before testing it.
In any case, I decided to use IBM XML Security Suite (XSS) again. You'll like the fact that for XML Signature, building a template programmatically proves almost as easy as using an XML file. Let's see how we can write code to generate all three types of digital signatures—enveloping, enveloped, and detached.
Template, template, template all around
XML Signature with XML Security Suite is easy once you have the hang of it. In Part 1, I wrote most examples using XML files for templates. For XML Signature, the examples are a bit more programmatic, but fret not: generating signatures programmatically is actually simpler in XSS. But before going into the code, we'll need to do some setup.
Prepare the necessary files
First, you need at least a pair of keys to generate signatures and verify them. Doing that is very easy—just use the good old keytool:
keytool -genkey -alias sheridan -keyalg RSA -keysize 1024 -keystore jw2store -storepass password -storetype jceks
When you execute the command above,
keytool will ask you several questions. It will use your answers to generate your certificate. Answer all the questions and you're set.