XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

Implementing XML Signatures in WSS4J
by Bilal Siddiqui | Pages: 1, 2

Implementing the Signature Tokens

In this section, I will demonstrate the implementation of different signature tokens discussed so far.

As you know WSS4J works on the idea of implementing the Token interface. Therefore, I will implement the Token interface for every token defined by Listings 2, 3, 4, 5, 6, and 7.

From the listings discussed in the previous section you can see that the Signature elements in Listings 2, 3, and 4 have exactly the same structure. Especially notice the following points regarding the ds:Signature element:

  1. The ds:Signature element in each listing contains a ds:SignedInfo element.
  2. The ds:SignedInfo element in Listings 2, 3, and 4 contains ds:CanonicalizationMethod, ds:SignatureMethod, and ds:Reference elements as its child.
  3. You can see from Listings 2, 3, and 4 that the ds:Signature element in each listing also contains the ds:SignatureValue and the ds:KeyInfo elements. The ds:KeyInfo element contains the wsse:SecurityTokenReference element, which in turn contains a wsse:Reference child element.

The features common among Listings 2, 3, and 4 lead us to implement a generic SignatureToken class that handles the common signing responsibilities presented by XML signing tokens. Then the different token classes will extend this generic SignatureToken for common functionalities and implement token specific additional functionality.

I have also written a SignatureTemplate XML file (shown in Listing 8) that contains the ds:Signature element structure, which we want to use as a signature template. You can notice from the ds:Signature element in Listing 8 that it is similar to the ds:Signature elements in Listings 2, 3, 4, 5, 6, and 7. The only difference is that the signature template has some empty fields:

  1. In Listing 8 the Algorithm attributes in the ds:CanonicalizationMethod, ds:DigestMethod, and ds:SignatureMethod elements are empty, while in Listings 2, 3, 4, 5, 6, and 7 the Algorithm attributes contains values.
  2. You can see in Listing 8 that the ds:SignatureValue element in the ds:Signature element wraps nothing, while in Listings 2, 3, 4, 5, 6, and 7 the ds:SignatureValue element contains the actual signature value.
  3. Listing 8 does not contain the ds:KeyInfo element in the ds:Signature element while Listings 2, 3, 4, 5, 6, and 7 contains the ds:KeyInfo element.

We will fill the missing values in the signature template during the signature process.

I will now discuss the implementation details of the SignatureToken class. Listing 9 shows the code for the SignatureToken class. You can notice the following points from Listing 9:

  1. The SignatureToken class implements the Token interface.
  2. The SignatureToken class contains two constructors, one with five parameters and the other with three parameters. The actual token classes that extend the SignatureToken class will decide which constructor they want to use.
  3. Following are the parameters that the five parameter constructors takes:
    • keyStoreFileName: The full path name of a Java key store file. The key store file contains the public key of the recipient of the WSS message.
    • keyStorePassword: The string representation of the password that we need in order to access the key store.
    • keyName: This parameter is actually the alias to the certificate. We will use the value of this parameter to extract the certificate from key store.
    • tokenWSUId: The identifier for the token used to sign the message.
    • wssMessage: The parent WSSMessage object that wraps this token.
  4. The SignatureToken constructor with five parameters performs the following steps:
    • First it sets the parameters in class-level variables. For example, it sets the value of the keyName parameter in the class variable keyName, the value of the tokenWSUId parameter in the class variable tokenWSUId, and the value of the wssMessage variable in the wssMessage class variable.
    • Then it loads the signature template (Listing 9) in a DOM Document object named templateRootEl.
    • Then it loads the KeyStore, whose path is specified in the keyStoreFileName.
  5. The SignatureToken constructor with three parameters takes the following parameters:
    • tokenWSUId: The identifier for token used to sign the message.
    • userName: The name of the user who is signing the message.
    • wssMessage: The parent WSSMessage object that wraps this token.
  6. The SignatureToken constructor with three parameters performs the following steps:
    • First it sets the parameters in class-level variables. For example, it sets the value of the tokenWSUId parameter in the class variable tokenWSUId, the value of the userName parameter in the class variable userName, and the value of the wssMessage parameter in the wssMessage class variable.
    • Then it loads the signature template (Listing 9) in a DOM document object named templateRootEl.
  7. The SignatureToken class will never be instantiated as it is not really an actual WSS4J token. It only represents the common functionality of several tokens. So you may ask why I am writing the constructors for the SignatureToken class. The answer to this argument is that the actual token classes will extend SignatureToken and their constructors will call the supers constructor.
  8. The SignatureToken class implements all the methods in Token interface in Listing 3 of the first article.
  9. Many Token interface methods are empty in the SignatureToken class. Only three methods of the Token interface have actual implementations in the SignatureToken class. These three methods are sign(), signWithXPath(), and setSecret(). We will shortly explain these three methods.
  10. The SignatureToken class (Listing 9) also contains two helper methods named loadDocument() and getXMLString().
  11. The loadDocument() method takes a XML structure in form of string, loads that string into a DOM document, and then returns the document instance to the calling method.
  12. The getXMLString() method is the opposite of the loadDocument() method. It takes a document, converts it into a string, and returns the string representation of the document.

Now have a look at the setSecret() method shown in Listing 9. The setSecret() method takes a secret in byte array form and uses the secret to fetch the private key that we will use for signing.

The setSecret() method performs the following steps:

  • First it extracts the X509 certificate from the key store.
  • Then it extracts the private key by calling the getKey() method of the KeyStore object.

Some of the token classes that extend the SignatureToken class will override the setSecret() method to implement their own token specific setSecret() method functionality.

Now have a look at the sign() method in Listing 9. This method authors the Signature element. The sign() method takes four parameters:

  • wsuElementID: The wsu:Id of the element that we are going to sign.
  • digestAlgo: The algorithm used to digest the message.
  • signatureAlgo: The signature algorithm used to generate the signature value.
  • canonicalizationAlgo: The canonicalization algorithm to be used before signing. Please refer to the Resources section for more details of canonicalization.

The sign() method performs the following nine steps (marked with comments in Listing 9) to fill in the blanks in the signature template:

Step 1: In this step, we import the ds:Signature element from the signature template document (Listing 9) into the DOM document which represents the parent WSS message.

Step 2: You can see in Listing 9 that the URI attribute of ds:Reference element in ds:Signature element is empty. So, we fill this with the value of the incoming parameter named wsuElementID. This URI attribute value refers to the wsu:Id of the element that we are going to sign.

Step 3: If you look at the signature template (Listing 8), you will find that there is an Algorithm attribute in ds:CanonicalizationMethod, ds:SignatureMethod, and ds:DigestMethod elements but the values are not specified. In this step we set the values of all three Algorithm attributes. The value of canonicalizationAlgo parameter is set in the Algorithm attribute of the ds:CanonicalizationMethod element. The value of the digestAlgo parameter is set in the Algorithm attribute of the ds:DigestMethod element. The value of the signatureAlgo parameter is set in the Algorithm attribute of the ds:SignatureMethod element.

Step 4: You can see from Listings 2, 3, 4, 5, 6, and 7 that each of them contains a ds:KeyInfo element. But our signature template does not contain any ds:KeyInfo element. Therefore, in this step, we insert a ds:KeyInfo element in the Signature element.

Step 5: Now we author the wsse:SecurityTokenReference element as the child of the ds:KeyInfo element.

Step 6: In this step we insert a wsse:Reference element as child of wsse:SecurityTokenReference element and also set its URI attribute with the wsu:Id of the security token (i.e. the fourth parameter named tokenWSUId that was passed to the SignatureToken constructor).

Step 7: Next, we create an instance of SignatureContext class. The SignatureContext class is explained in fourth article of the Web Services Security for Java series.

Step 8: Now let's make a call to the setIDResolver() method of the SignatureContext class and pass it the WSUIdResolver object. The IDResolver class from XSS4J cannot resolve wsu:Id of an element. Therefore, we have implemented our own id resolver class named as WSUIdResolver. I will discuss the details of WSUIdResolver class after the discussion of the SignatureToken class.

Step 9: Now we are ready for signing. We can call the sign() method of SignatureContext by passing the signature template element (i.e. populated in above stated steps) and a private key (that was mentioned while discussing the setSecret() method).

The SignatureToken class of Listing 9 contains another public method named signWithXPath(). This method also signs a WSS message like the sign() method discussed above. The difference comes between both methods while referencing the element to be signed in the WSS message.

The signWithXPath() method references the element to be signed with an XPath expression. The signWithXPath() method takes the same parameters as discussed for the sign() method above except the first parameter, which is the XPath expression that identifies the element to be signed.

The implementation steps for the signWithXPath() method are same as discussed for the sign() method except the third and eighth steps:

Step 3: This time the URI attribute of ds:Reference element will remain empty. Instead, we author a Transforms child of the Reference element to resolve the XPath expression. This XPath expression will identify the element from the WSS message needed to be signed.

Step 8: We do not need any IDResolver class to resolve the wsu:Id. Therefore, Step 8 is empty in the signWithXPath() method.

When we sign using XPath, the resulting Signature element will look like Listing 10. You can see that Listing 10 is similar to the Signature elements in Listings 2, 3, 4, 5, 6, and 7. The only difference is that in Listing 10 there is a Transforms child element of the ds:Reference element.

Now have a look at Listing 11 that shows the code for WSUIdResolver class. As mentioned in Step 8 of discussion on the sign() method, the SignatureContext class will use the WSUIdResolver class to resolve the wsu:Id of the element to be signed.

XSS4J provides a class named as AdHocIDResolver to resolve the Ids of elements in a SOAP message. This AdHocIDResolver class implements an interface named IDResolver. The IDResolver interface contains just one method named resolveID(). Whenever the SignatureContext class comes across the URI attribute in the Reference element, it calls the resolveID() method , which returns the element to be signed.

But there is a problem, the IDResolver class from XSS4J cannot resolve the wsu:Id. Therefore, we have implemented our own class named WSUIdResolver in Listing 11 to resolve the wsu:Id. The WSUIdResolver class implements the resolveID() method, which will return the element we want to sign.

Note the following points from the resolveID() method in Listing 11:

  1. The resolveID() method takes a DOM document object and the wsu:Id to be resolved as parameters. The DOM document object is the input document in which the resolveID() method will search for the element to be signed.
  2. It extracts the root element from the document passed to it.
  3. Then it creates an XPath query to search the element in the input DOM document whose wsu:Id matches with the wsu:Id we are looking for.
  4. Then resolveID() method runs the XPath query on the input DOM document and returns the matching element.

The next discussion will explain the implementation of actual signature tokens.

Signing a WSS Message Using X509 Binary Security Token

Listing 12 shows the code for the BinarySecurityTokenWithReference class that implements the X509 binary security token of Listing 2. Following points explain the code shown in Listing 12:

  1. The BinarySecurityTokenWithReference class extends the SignatureToken class.
  2. The constructor of the BinarySecurityTokenWithReference class makes a call to the super's constructor.
  3. The BinarySecurityTokenWithReference implements a getXMLString() method. The getXMLString() method authors the XML for the token and returns it in string form. The wsse:BinarySecurityTokenelement in Listing 2 shows the typical XML string that this method authors.
  4. You can see in Listing 12 that the BinarySecurityTokenWithReference also implements a public method named getType(), which returns the type of token in string form. This String uniquely identifies the X509 binary security token.

Signing a WSS Message Using X509 Pointer Token

Now have a look at Listing 13 that shows the code for X509CertificatePointerToken class, which implements the X509 pointer token described in Listing 6. The X509CertificatePointerToken class is similar to the X509BinarySecurityTokenWithReference class except that it does not implement the getXMLString() method and overrides its own sign() method.

You can notice in Listing 13 that all the steps of the sign() method of X509CertificatePointerToken are the same as the steps in the sign() method of the SignatureToken class except Step 6:

Step 6 of Listing 13: In this step we insert an ds:X509Data element as child of SecurityTokenReference element. We have discussed the details of ds:X509Data element while discussing the Listing 6.

Signing a WSS Message Using X509 Key Name Token

Now have a look at Listing 14 that shows the code for KeyNameToken class, which implements the X509 key name token that we described in Listing 5. The KeyNameToken class is similar to the X509CertificatePointerToken class in Listing 13 except step 6 that is described below:

Step 6 of Listing 14: In this step we insert a ds:KeyName element as child of wsse:SecurityTokenReference element. This ds:KeyName element wraps the alias that uniquely identifies an X509 certificate.

Signing a WSS Message Using Username Text Password Token

Now have a look at Listing 15 that shows the code for UserNamePasswordTextTokenWithReference class that implements the username password text token, which I described in Listing 3. The UserNamePasswordTextTokenWithReference class is similar to the BinarySecurityTokenWithReference class (Listing 12) except that it implements its own setSecret() method. The setSecret() method takes a secret in byte-array form and builds a secret key based on the secret byte array.

Signing a WSS Message Using Username Digest Password Token

Now have a look at Listing 16 that shows the code for UserNamePasswordDigestTokenWithReference class that implements the username password digest token, described in Listing 4. The UserNamePasswordDigestTokenWithReference class is similar to the UserNamePasswordDigestTokenWithReference class (Listing 15) except that it implements its own getXMLString() method as described below:

  1. You can see in Listing 16 that the getXMLString() method first generates an eight byte random number.
  2. Then it encodes the random number in base-64, which forms a nonce value.
  3. Then it gets the current system time and converts it into UTC format, which is a WSS requirement. This forms a time stamp.
  4. Then it concatenates the nonce, timestamp, and a base-64 encoded secret key. The token already knows the secret key from the setSecret() method shown in Listing 16.
  5. Then it calculates the SHA-1 digest value over the result of Step 4.
  6. Now it applies base-64 encoding to the result of Step 5. This is the required digest value, which it wraps inside the wsse:Password element as shown in Listing 4.

In this article my focus of discussion was on tokens. I discussed six different security tokens and demonstrated the implementation details of five of them in Java. I discussed the XML representation of X509 key identifier token in Listing 7 but have not yet implemented this token in Java.

Resources