Yes, you can secure your Web services documents, Part 1

XML Encryption keeps your XML documents safe and secure

Web services! Chances are you've already developed some Web services yourself, or at the very least have heard something about them. While people have put forth numerous Web services definitions, the vision remains the same: interconnect everything from your handheld device and your PC to that big black box in that cold room over there. Indeed, a Web service world could prove more egalitarian in that the distinction between clients and servers blurs. Anyone can be a server or a client—it's just a matter of who possesses something and who wants something.

Call me paranoid, but my first question when I learned about the Web services vision was: "How do they secure this thing?" In a Web services world, everyone communicates with everyone else. Many intermediaries could exist between, say, your supplier, you, and your buyer. What if one of these intermediaries becomes compromised? The communication path then proves only as secure as the least secure hop along the way. End-to-end security becomes vitally important if you want to do something more significant than those five-minute, 10-lines-of-code Hello World services in the tutorials. And the security questions don't stop there: What about authentication? Trust? Authorization?

Microsoft, IBM, and VeriSign have introduced specifications to answer questions ranging from privacy to trust to federation. Underlying these higher-level specifications resides WS-Security (Web Services Security), which describes enhancements to SOAP (Simple Object Access Protocol) messaging to include message integrity and message confidentiality. These two basic features, in turn, stem from the topic of this two-part series: XML Encryption and XML Signature.

Read the whole series on XML security, "Yes, You Can Secure Your Web Services Documents:"

Note: You can download this article's source code from Resources.

Why XML Encryption?

Standards like SSL/TLS (Secure Socket Layer/Transport Level Security) for point-to-point security already exist. S/MIME (Secure/Multipurpose Internet Mail Extensions) and PGP (Pretty Good Privacy) ensure that only a message's intended recipient can read that message—thereby achieving end-to-end security. Considering that, what gap does XML Encryption fill?

It turns out that (fortunately) the World Wide Web Consortium (W3C) XML Encryption Working Group did have good reasons for XML Encryption. XML Encryption addresses two requirements that none of the above standards can satisfy together: end-to-end security and selective encryption.

End-to-end security

If you've ever worked in the payment domain, you no doubt have noticed that the big players never feel satisfied with SSL/TLS. They keep coming up with these so-called secure payment specifications, from SET (Secure Electronic Transaction), which didn't take off, to the newer ones like Visa's 3D Secure and MasterCard's SPA-UCAF (Secure Payment Application/Universal Cardholder Authentication Field).

Why? Partly because an online credit card payment involves three parties: a cardholder (that's you), an online merchant, and your bank. Ideally, all parties should be authenticated to one another, and the secure communication path should stretch from the cardholder all the way to the bank. But SSL/TLS is simply not designed for such a scenario; SSL/TLS only handles point-to-point security. Thus, if you rely only on SSL, you cannot send your credit card number to the bank through the merchant without the merchant knowing it.

The same goes for Web services, although at a slightly different level. Web service application topologies include all sorts of devices, PCs, proxies, demilitarized zones, gateways, and who knows what else. Consequently, many intermediaries come between two communicating parties. SSL/TLS may secure the path between any two, but not from one end to the other. Something new, therefore, must address this problem.

Selective encryption

Standards like S/MIME and PGP do ensure that a message can only be read by its intended recipient. Unfortunately, these standards (as well as SSL/TLS) treat each message as a whole; these standards cannot selectively encrypt part of an XML document. Such selective encryption capabilities will prove important, especially when it comes to a workflow scenario where a document may be processed by several applications, or signed, viewed, or approved by numerous people.

Let's take as an example the aforementioned online payment scenario. In practice, many messages travel to and fro among the three parties before the payment itself happens, but conceptually, what happens proves simple: a message travels from the consumer to the merchant to the bank. Such a message may contain two things: information about the goods bought and instructions for the bank to pay the merchant. The bank doesn't need to know about the former, and the merchant doesn't need to know about the latter. The message, therefore, must be partitioned in such a way that a merchant cannot see the bank's part of the message, and vice versa. This is difficult to achieve; it's impossible with S/MIME or PGP because these standards will encrypt the entire XML document. XML Encryption's selective encryption feature, in contrast, allows an XML-based solution for such problems.

XML Encryption: The core element

In XML Encryption, your plaintext is either an element or that element's content (that's the finest granularity you get—you can't encrypt, say, half an element's content). After encryption, you get an XML element called EncryptedData, containing the ciphertext in Base64-encoded format. That EncryptedData element replaces your plaintext. That is, if you encrypt element B in this snippet below:

<A>
    <B>blah</B>
</A>

you'll get back something like this:

<A>
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns=...>
    <!-- some info, including the ciphertext -->
</EncryptedData>
</A>

Whereas if you encrypt element B's content, the result will look similar to this:

<A>
<B>
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Content" xmlns=...>
    <!-- some info, including the ciphertext -->
<EncryptedData>
</B>
</A>

Note the difference between the Type attributes in the two examples. From looking at this attribute, we can tell immediately whether the plaintext is an element of just its content.

EncryptedData's structure proves straightforward. I copied and pasted the structure below directly from XML Encryption's W3C page. In this structure, according the W3C, "? denotes zero or one occurrence; + denotes one or more occurrences; * denotes zero or more occurrences; and the empty element tag means the element must be empty":

<EncryptedData Id? Type?>
    <EncryptionMethod/>?
    <ds:KeyInfo>
        <EncryptedKey>?
        <AgreementMethod>?
        <ds:KeyName>?
        <ds:RetrievalMethod>?
        <ds:*>?
    </ds:KeyInfo>?
    <CipherData>
        <CipherValue>?
        <CipherReference URI?>?
    </CipherData>
    <EncryptionProperties>?
</EncryptedData>

EncryptedData's most important element, CipherData, either directly contains the ciphertext in CipherValue, or, if CipherReference is used, a reference to it. The other elements are optional because usually the receiving party already has the information they contain. For instance, EncryptionMethod lets you specify the algorithm and key size, but usually you and the other party will agree on those beforehand. The same goes for KeyInfo: it gives you the flexibility to give the other party the material to decrypt your message, but you'd probably want to, say, sent it through some out-of-band mechanism. EncryptionProperties serves as another optional element used for, well, optional information, such as a date/time stamp.

What happens if you want to do super-encryption? That is, what if you want to encrypt your ciphertext with a different key, for instance? The standard mandates that you encrypt the whole EncryptedData element, nothing more, nothing less. As a result, inside the CipherData element, you don't get the result of encrypting just your ciphertext, but the whole EncryptedData element around it as well.

Libraries you can use now

XML Encryption is very new. IBM has submitted a Java Specification Request (JSR 106) that will define its standard API. Currently, however, no standard exists on how the API should look. At the time of this writing, only a handful of libraries provide XML Encryption. I use one of them, the XML Security Suite from IBM (XSS4J), in the rest of this article for illustration purposes. (Don't forget to check the sidebar below, "Give Your Serializable Class a serialVersionUID," to learn about something weird I encountered the first time I tried this library.)

XML Encryption in practice

To see XML Encryption in practice, I'll use it on the document in Listing 1 below. Let's pretend that this example document represents a payment request in the latest payment protocol someone just designed:

Listing 1

<PurchaseOrderRequest>
   <Order>
      <Item>
         <Code>C3PO</Code>
         <Description>Some almost illegal stuff</Description>
      </Item>
      <Quantity>1</Quantity>
   </Order>
   <Payment>
      <CreditCard>
         <BrandId>Brand X</BrandId>
         <Number>1111222233334444</Number>
         <ExpiryDate>20030408</ExpiryDate>
      </CreditCard>
      <PurchaseAmount>
         <Amount>123623</Amount>
         <Currency>840</Currency>
         <Exponent>-2</Exponent>
      </PurchaseAmount>
   </Payment>
</PurchaseOrderRequest>

Suppose you want to encrypt two things with two different keys: you'll encrypt the Item element's content using the merchant's key, and the entire CreditCard element using the bank's key. What do you need? You need two keys and the code to encrypt the document, of course. Let's get 'em!

Generate the keys

Let's generate two keys and put them into a JCE (Java Cryptography Extension) keystore. Using keytool, type the following command:

keytool -genkey -alias merchant -keyalg RSA -keysize 1024 -keystore jwstore -storepass jwpassword -storetype jceks

and answer the questions that ensue to generate a key for the merchant. Let's use merchantpassword as the password. Do the same for the bank by running the utility again, supplying bank as the alias and bankpassword as the password.

Write the code

Now let's look at the encrypting code. XSS4J's API may look a bit complex at first, but you'll find it easy once you figure out how it works. Think about it—if I asked you to encrypt an element, what information would you need? You'd need the following:

  1. The element to encrypt
  2. Whether to encrypt the whole element or just its content
  3. The encryption method to use (that is, key size and algorithm)
  4. The key to use in encrypting
  5. A way to get the key

I've written a helper class that, well, helps you to do just those things. With this class, encrypting XML documents is easy:

Listing 2

   // Open a document we're going play with.
   XMLPlainText xmlPlainText = new XMLPlainText(
    new FileInputStream("payment.xml"));
   
   // Let's load the keystore.
   KeyStore ks = KeyStore.getInstance("jceks");
   ks.load(new FileInputStream("jwstore"), "jwpassword".toCharArray());
   // Tell the class how to get 
   // the keys. KeyStoreKeyInfoResolver is just 
   // one way to get the keys.
   KeyStoreKeyInfoResolver kskiResolver = new KeyStoreKeyInfoResolver(ks);
   kskiResolver.putAliasAndPassword("merchant",    
    "merchantpassword".toCharArray());
   kskiResolver.putAliasAndPassword("bank", "bankpassword".toCharArray());
   // Prepare the encrypted data, 
   // put CipherValue in it.
   EncryptedData ed = new EncryptedData();
   ed.setCipherData(prepareCipherData());
   // Use RSA v1.5 to encrypt, an unusual choice.
   // Usually you'd use a 3DES key 
   // to encrypt, and the RSA
   // key to encrypt the 3DES key.
   EncryptionMethod rsa_1_5 = prepareEncryptionMethod(EncryptionMethod.RSA_1_5);
   // Prepare the keys.
   KeyInfo merchant = prepareNameOnlyKeyInfo("merchant");
   KeyInfo bank = prepareNameOnlyKeyInfo("bank");
   
   // Encrypt!
   xmlPlainText.encrypt("//Item", kskiResolver, ed,
    EncryptedData.CONTENT, rsa_1_5, merchant);
   xmlPlainText.encrypt("//CreditCard", kskiResolver, ed, 
    EncryptedData.ELEMENT, rsa_1_5, bank);
   // Dump it!
   PrintWriter pw = new PrintWriter(
    new FileWriter("encryptedPayment_rsa_1.xml"));
   pw.write(xmlPlainText.toString());
   pw.close();

Listing 3's encryptedPayment_rsa_1.xml shows the result:

Listing 3

<?xml version="1.0" encoding="UTF-8" ?>
<PurchaseOrderRequest>
   <Order>
      <Item>
         <EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#">
            <CipherData>
               <CipherValue>BNjivf7gTOhHmcfZIX8XI6Vh06UFjwL9HaXMHD+CoAqa4A14UJsh+m9VFNEb5
OezHpAGP1TpZDEo3mHhvo4hcMd2C01z9HkmVLgEdGn77fC/aSmrIb+NjqkvMGxIg/AaUFrv79RsdQBKs
cg7pXRRhdgB5h4JSxHJ7dlZudnZBrg=</CipherValue>
            </CipherData>
         </EncryptedData>
      </Item>
      <Quantity>1</Quantity>
   </Order>
   <Payment>
      <EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#">
         <CipherData>
            <CipherValue>ci3q/7BqV92eizpxvIszQeo6GYB1NOfePoFiUvJtsYMkhrvR39cqUpdg07J3Q
3GzCymyrXwtZ9iaV4XzzCuvaTYiTecorvLypGcl1NOerWTV/RWmDU04hHHxE0WyvAVML9r/VhMTzHUVx
SR2ZkaHe5rzD9NVR0YgApB1IPlOy1m6SxqLeAHC41/apIYaCIq3HUoFqCQuK/1x/OAoPr5jvLjq2ikUg
tqtJsdzihPSYmNNzOOxcZRiD6/O5OeN1Pe5FqJ9Q6RR26vDJNwAXf+srf8hXfIvGrx3yTQ88Sp2//oih
toH55NZnjns78i4bNGlGoyNK4GTG9DTGEL0+QRTxA==</CipherValue>
         </CipherData>
      </EncryptedData>
      <PurchaseAmount>
         <Amount>123623</Amount>
         <Currency>840</Currency>
         <Exponent>-2</Exponent>
      </PurchaseAmount>
   </Payment>
</PurchaseOrderRequest>

Easy, isn't it? It gets easier in the next section!

A simpler way to write the code

If you examine Test.java (found in this article's source code), you'll see that its code mostly comprises prep lines—code that prepares the objects you need to pass to encrypt(). It worsens for more complex structures like KeyInfo for RSA and 3DES (Triple Data Encryption Standard) keys. Fortunately, with XSS4J, you can use the actual XML structure itself instead of the corresponding class's instance. Look at the code snippet below. In this example, I want the RSA key to encrypt the 3DES key (which encrypts the Item element's content), but I'm too lazy to write the code to create the cumbersome KeyInfo structure required for this operation:

Listing 4

   // Open a document we're going play with.
   XMLPlainText xmlPlainText = 
    new XMLPlainText(new FileInputStream("payment.xml"));
        
   // Load the keystore.
   KeyStore ks = KeyStore.getInstance("jceks");
   ks.load(new FileInputStream("jwstore"), "jwpassword".toCharArray());
   // Tell the class how to get
   // the keys. KeyStoreKeyInfoResolver is just 
   // one way of getting the keys.
   KeyStoreKeyInfoResolver kskiResolver = new KeyStoreKeyInfoResolver(ks);
   kskiResolver.setAlgorithmFactory(new AlgorithmFactoryExtn());
   kskiResolver.putAliasAndPassword("merchant", 
    "merchantpassword".toCharArray());
   kskiResolver.putAliasAndPassword("bank", "bankpassword".toCharArray());
   // Get EncryptedData.
   Element edEl =       
    XMLUtil.getDocumentFromFile("encrypted_data.xml").getDocumentElement();
   EncryptedData ed = new EncryptedData(edEl);
   // Encrypt!
   xmlPlainText.encrypt("//Item", kskiResolver, edEl, EncryptedData.ELEMENT, 
    null, null);
   // Dump it!
   PrintWriter pw = new PrintWriter(
    new FileWriter("encryptedPayment_des_rsa.xml"));
   pw.write(xmlPlainText.toString());
   pw.close();

With the above code, the Item's content element is encrypted into Listing 5's EncryptedData structure:

Listing 5

<?xml version="1.0" encoding="UTF-8" ?>
<PurchaseOrderRequest>
   <Order>
      <Item>
         <enc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
            xmlns:enc="http://www.w3.org/2001/04/xmlenc#">
            <enc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
            <dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
               <enc:EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
                  <enc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
                  <dsig:KeyInfo>
                     <dsig:KeyName>merchant</dsig:KeyName>
                  </dsig:KeyInfo>
                  <enc:CipherData>
                     <enc:CipherValue>SbI2DZ+WcYXbGEwcy43hpp9xtULN0TK3PF5+eFh7woEtXrSv2eMjwlMFQ
0x0uxsNHKhHNvlLIltbOUQnTZCItv1PCgxowd3v8Ttu7Wvnbg9wK8vfTJtmIttXunLQhgz/EAa4bALbQ
sfs7u6yKzPFB4NQhGgy7ghsSgO1fQSbSBs=</enc:CipherValue>
                  </enc:CipherData>
               </enc:EncryptedKey>
            </dsig:KeyInfo>
            <enc:CipherData>
               <enc:CipherValue>RfEyBjhg7KbRXWRbVPNGKuwUwLsIo+gRZHJ/kmSaZkWyjl3SjtCmYZ3PW
bZGLWYHekjKW113Ryy7gRZhOe7RECv7kR+GYO969+DXz3eWbfKQ2p6yc+CR35q06+Qggy/9</enc:
CipherValue>
            </enc:CipherData>
         </enc:EncryptedData>
      </Item>
      <Quantity>1</Quantity>
   </Order>
   <Payment>
      <CreditCard>
         <BrandId>Brand X</BrandId>
         <Number>1111222233334444</Number>
         <ExpiryDate>20030408</ExpiryDate>
      </CreditCard>
      <PurchaseAmount>
         <Amount>123623</Amount>
         <Currency>840</Currency>
         <Exponent>-2</Exponent>
      </PurchaseAmount>
   </Payment>
</PurchaseOrderRequest>

I used Listing 6's template below to generate Listing 5's encrypted document. Notice how closely the template resembles Listing 5's EncryptedData structure:

Listing 6

<?xml version="1.0" encoding="UTF-8" ?>
<EncryptedData Type="...#Element" xmlns="...">
   <EncryptionMethod Algorithm="...#tripledes-cbc" />
   <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
         <EncryptionMethod Algorithm="http://www.w3.org/2001/04/
            xmlenc#rsa-1_5" />
         <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <KeyName>merchant</KeyName>
         </KeyInfo>
         <CipherData>
            <CipherValue />
         </CipherData>
      </EncryptedKey>
   </KeyInfo>
   <CipherData>
      <CipherValue />
   </CipherData>
</EncryptedData>

Look at the EncryptedKey element above. It closely resembles EncryptedData, doesn't it? Indeed, both derive from an abstract XML Schema type called EncryptedType. The main difference between the two (no prize for guessing this): one encrypts keys, the other data. In the example above, the CipherValue's content inside EncryptedKey results from encrypting the newly generated 3DES key with the merchant's RSA key.

Retrieve your plaintext

Decrypting your encrypted XML is easy. You tell the library which node it should decrypt and how it should obtain the key to encrypt. That information, of course, depends on whether you're just using an RSA key in the keystore or a Triple DES key included in the EncryptedData. If your (partially) encrypted XML document resembles the one in Listing 2, which contains no information about the encryption algorithm or the key, naturally you must supply them, like this:

Listing 7

   EncryptionMethod em = new EncryptionMethod();
   em.setAlgorithm(EncryptionMethod.RSA_1_5);
   KeyInfo ki = new KeyInfo();
   KeyName kn = new KeyName();
   kn.setName("merchant");
   ki.addKeyName(kn);
   // Decrypt the one encrypted using the merchant's key
   xmlCipherText.decrypt("//Item/child::*", kskiResolver, 
   null, null, 
   em.createElement(XMLUtil.createNewDocument(), true), 
   ki.createElement(XMLUtil.createNewDocument(), true));
   KeyInfo ki2 = new KeyInfo();
   KeyName kn2 = new KeyName();
   kn2.setName("bank");
   ki2.addKeyName(kn2);
   // Decrypt the one encrypted using the bank's key
   xmlCipherText.decrypt("//Payment/child::*[position()=1]", kskiResolver, 
   null, null, 
   em.createElement(XMLUtil.createNewDocument(), true), 
   ki2.createElement(XMLUtil.createNewDocument(), true));

On the other hand, if you're using Listing 6's template, which contains the EncryptedKey, you must supply an EncryptedKeyRetriever so the library knows how to retrieve your key. Yet you don't need to supply the key information and encryption method, because they're already inside EncryptedData. The library needs an EncryptedKeyRetriever able to extract that encrypted key. In this example, because the encrypted key is in the same document, we use SameDocumentEncryptedKeyRetriever, which handles just that:

Listing 8

   EncryptedKeyRetriever ekr = 
    new SameDocumentEncryptedKeyRetriever(
     xmlCipherText.getCurrentNode().getOwnerDocument());
   // Decrypt!
   xmlCipherText.decrypt("//Item/child::*", kskiResolver, ekr, id, null, null);

Things to look forward to

In this article you've learned why XML Encryption exists, the problems it addresses, and the existing protocols' shortcomings in a Web services environment. You've also seen what your XML documents look like after encryption, with all the variations in the structure, from the simplest structure to the complex ones with encrypted keys inside. You've also seen how you can achieve all this with XSS4J, IBM's XML Security Suite, by looking at the sample code sprinkled throughout the article.

But XML Encryption boasts other interesting features, especially when used with XML Digital Signature, another important standard upon which WS-Security rests. XML Encryption alone won't prove useful if it can't handle scenarios such as workflow, in which both encryption and signature can happen in any order. That's why the W3C's XML Encryption Working Group designed Decryption Transform, which I'll discuss, along with XML Digital Signature, in Part 2 of this series. Till then, have fun with XML Encryption! Try out the sample code; all the files are under an Eclipse project, so you can just open the project directly and try them out after you've set everything up. Enjoy!

Ray Djajadinata first encountered Java in 1995 when he saw Duke cartwheeling inside the HotJava browser. He then wrote introductory Java articles for a local programming magazine and explored the language further while still using C++ at work. In 1999, he switched to Java at work as well, and he's been hooked ever since. Currently he's a senior technical manager at eXtropia, an open Web technology company. He likes eXtropia because the company encourages employees to embrace new technologies. He also gets to work on cool products like the eXtropia Digital Signing application, which triggered his research about the topic of this series.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies