Signing Messages with XSS4J
June 2, 2004
In the first column of this series we introduced the WSS4J API. In the second column we demonstrated the use of XSS4J for XML encryption. In the third column we implemented encryption features of WSS4J using the concepts discussed in the second column.
This column will examine the implementation of the reference list only token that we introduced in Listing 4 of the third column, as well as the use of XSS4J to sign XML messages.
Implementing the reference list only token
As you may recall from the third column, implementing a particular token needs two steps:
- Implement the Token interface to reflect the functionality of the token.
- Add token-specific handling in the
encrypt()
method of theWSSMessage
class.
You can refer to Listing 4 of the previous column to see the format of the WSS message that a reference list only token will create.
Listing 1 shows the
ReferenceListOnlyToken
class which implements the Token
interface for reference list only token. The ReferenceListOnlyToken
implementation is similar to the EncryptedKeyToken
implementation that we
presented in Listing
5 of the previous column. You can compare Listing 1 of this column with Listing 5 of the
previous column. You will notice that the ReferenceListOnlyToken
class is
simpler than the EncryptedKeyToken
class. Notice the following points from Listing 1:
- This time we are not using the
KeyStoreKeyInfoResolver
object because we don't want to include any encrypted key. Here we just need to wrap the name of the key inside the KeyInfo-KeyName pair. We can easily do it using DOM, so there's no need to use theKeyStoreKeyInfoResolver
object. - The encryption template (Listing 2)
that we are using for the reference list only token contains the KeyInfo-KeyName pair.
The
ReferenceListOnlyToken
constructor authors the name of the encryption key as contents of the KeyName element. - The encrypt() method in Listing 1
performs the actual encryption. The process of encryption is similar to the
EncryptedKeyToken.encrypt()
method in Listing 5 of the previous column, except that Listing 1 does not use theKeyStoreKeyInfoResolver
object. Recall that step 6 of theencrypt()
method (Listing 5 of the third column) made a call to theEncryptionContext.setKeyInfoResolver()
method. Now in Listing 1, theencrypt()
method of theReferenceListOnlyToken
class makes a call to theEncryptionContext.setKey()
method. This is because we are not using theKeyStoreKeyInfoResolver
class and therefore we need to provide the key directly to theEncryptionContext
class. - The
getXMLString()
method in Listing 1 authors the actual token (aReferenceList
element, as shown in Listing 4 of the previous column). Notice that thegetXMLString()
method of Listing 1 authors the token from scratch. While in theEncryptedKeyToken.getXMLString()
method (Listing 5 of the previous column), the token was part of the encryption template, and we extracted the token from theEncryptedData
element in step 7 of theencrypt()
method. You can see that authoring aReferenceListOnlyToken
is simpler than authoring theEncryptedKeyToken
.
Listing 3 shows an enhanced version of the
WSSMessage
class, which includes support for ReferenceListOnly
token, in addition to the tokens that we implemented in the previous column.
Listing 4 is a small sample application
that shows how you will use the ReferenceListOnly
token in a Java application.
The use is exactly the same as before:
- Instantiate a
WSSMessage
object. - Instantiate a
Token
implementation and call itssetSecret()
method. - Call the
encryptElement()
method of theWSSMessage
object that you created in step 1.
Using XSS4J for XML Digital Signature
We are now going to demonstrate how to use XSS4J to produce XML digital signatures. We have already discussed all details of the XML Digital Signature syntax in the second article of my series on Web services security.
We will use the following different types of KeyInfo elements in our demonstration:
- KeyInfo element that wraps an X.509 certificate
- KeyInfo element that wraps pointers to an X.509 certificate
- KeyInfo element that wraps the name of a key
We are going to sign the first Parameter element of the XML file shown in Listing 5. After signing, the resulting XML will look like as shown in Listing 6.
Notice that the signature that we are going to produce is detached from the element that we signed. Refer to the three types of XML digital signatures (enveloping, enveloped, and detached) that we discussed in the "Message Integrity and User Authentication with XML Signatures" section of second article of my Web services security series.
We are going to demonstrate the use of XSS4J to produce detached signatures. This is because WSS only allows detached type of XML signatures in WSS messages.
The XMLDSigSampleWithCertificate
class that we have shown in Listing 7 shows how to use XSS4J to produce
detached Signatures. We have hard-coded the following parameters in Listing 7:
-
keyStorePath
is the full path name of a key store file. The key store file contains the certificate that we going to use in our sample application. The source code download of this column contains a generateCertificate.bat file that shows how you can generate a certificate to try our sample application. -
alias
is the string representing the name of the certificate in the key store that we are going to use in our sample signature application. -
storePass
is the string representation of the password that we need in order to access the key store. -
keyPass
is the string representation of the password that we need in order to access the private key in the key store. -
signatureTemplateFile
is the name of the template file (Listing 8) that we are going to use for signature. You can compare Listing 8 with Listing 6 and you will find that the template does not contain the digest and signature values. These values will be filled in during the signature process. Also notice that the signature template does not contain a KeyInfo element. That's because we will author a KeyInfo element programmatically and append the same into the template. -
inputFile
is the string containing name of the input XML file (Listing 5). -
outputFile
is the string containing name of the output XML file, which shows the result of signature operation (Listing 6).
You can find the following simple steps in Listing 7:
Step 1: Load the input XML file in a DOM Document object named inputDoc
.
Step 2: From the DOM document of step 1, find the element to be signed. We are simply
going to sign the first Parameter child of the
GetSpecialDiscountedBookingForPartnersResponse
element of Listing 5, so the second step in the
XMLDSigSampleWithCertificate
class of Listing 7 is to fetch the root element of
the input XML file (which is the GetSpecialDiscountedBookingForPartnersResponse
element) and then fetch its first child element (which is the Parameter element that
we are
going to sign).
Step 3: After loading the element to be signed in a DOM element object, we have also
fetched the "Id" attribute value of the element to be signed and stored the value
in a
variable named elementToBeSignedId
. We will (later in step 5) use this value to
refer from the Signature element to the element that we are going to sign.
Step 4: Load the signature template (Listing 8) in a DOM document named templateDoc
and then extract the
Signature element from the templateDoc
. We have stored the Signature element in
a DOM Element object named templateElement
. Note that we have imported the
templateElement
into the inputDoc
and placed it as a child of
the GetSpecialDiscountedBookingForPartnersResponse
element. Now the template
Signature element is at its correct place in the input XML file. The signature process
will
only fill the template with actual signature data.
Step 5: Now we can set the id attribute value (that we fetched in step 3 above) as
the value of the URI
attribute of the Reference
element. Notice
from the Signature template of Listing 8
that there is a Reference child element of the Signature element. The Reference
element has an attribute named URI
, whose value we have not mentioned in the
template because we want to set this value dynamically. This URI
attribute is
used to refer from the Signature to the element that we are signing. For details of
how the
URI
attribute works, please refer to point number 1 of the "Four Steps to XML
Digital Signature Authoring" section of the second article of my series on
web services security.
Step 6: Next we get an instance of the KeyStore
class and load a key
store file into the KeyStore
object. Here we are assuming that the key store
file contains a certificate named "myTourOperatorCertificate"
. So we simply
call the getCertificate()
method of the key store object, which returns an
X509Certificate
instance. The X509Certificate
instance wraps the
tour operator's certificate. After loading the tour operator's certificate in an
X509Certificate
object, we also fetch the public key of the tour operator and
load it into a Key
object.
Step 7: Now we want to author a KeyInfo element. For this purpose, XSS4J provides a
class named KeyInfo
. The KeyInfo
class offers functionality for
easy authoring of a variety of KeyInfo elements (e.g. KeyInfo elements that wrap X.509
certificates or PGP data).
We are only concerned with how to author KeyInfo elements that wrap X.509 certificates.
In
order to wrap an X.509 certificate inside a KeyInfo element, XSS4J provides a class
named
KeyInfo.X509Data
.
The KeyInfo.X509Data
class can wrap certificates inside a KeyInfo element in
a variety of ways. At the moment, we want to demonstrate the authoring of a KeyInfo
element
that wraps the actual binary content of a single certificate. We will shortly demonstrate
how to author KeyInfo elements that wrap pointers to a certificate instead of the
actual
binary content of a certificate.
So our seventh step is to instantiate a KeyInfo.X509Data
element and call the
setCertificate()
method of the KeyInfo.X509Data
class, passing
the X509Certificate
object of step 6 along with the method call. This will set
the binary content of the X509Certificate
object of step 6 into the
KeyInfo.X509Data
object.
Step 8: Once you have the KeyInfo.X509Data
object loaded with the
correct certificate, you need to add the KeyInfo.X509Data
object into the
KeyInfo
object. For this purpose, you will instantiate a new
KeyInfo
object and then call its setX509Data()
method. The
setX509Data()
method takes an array of KeyInfo.X509Data
objects.
This method adds all the KeyInfo.X509Data
objects in the array to the
KeyInfo
object. More than one KeyInfo.X509Data
objects in the
array represent a chain of certificates that may be needed to certify each other,
ending at
the certificate of the signer.
However, we have only one certificate to wrap in a KeyInfo element, so we have formed
an
array of KeyInfo.X509Data
objects with just one KeyInfo.X509Data
object and passed the array to the KeyInfo.setX509Data()
method.
Step 9: Now we have the KeyInfo element, which should be added to the signature template (the templateElement from step 4).
Step 10: After adding the KeyInfo to the signature template, we are ready for XML
digital signature. To produce the signature, we need a SignatureContext
object.
Therefore, the next step is to instantiate a SignatureContext
object.
Step 11: Next, we instantiate an AdHocIDResolver
object and pass the
object to the SignatureContext.setIDResolver()
method. The
SignatueContext
class will use the AdHocIDResolver
class to find
the element to be signed.
Recall from step 5 that we have set the URI
attribute value of the Reference
element. The URI
attribute value matches with the Id
attribute
value of the element that we are going to sign. Note that the Reference element is
the child
of the Signature element, which we stored in the templateElement
node in step
4. In the next step (step 12) we will pass the templateElement
node to the
SignatureContext.sign()
method. The sign()
method will
internally resolve (or dereference) the URI
attribute value of the Reference
element and find the element that we are signing. The AdHocIDResolver
class
helps in resolving the Id
to the element that we are signing.
Step 12: Now we can produce the required signature by calling the
SignatureContext.sign()
method. The sign()
method takes two
parameters, namely the signature template element (the templateElement
from
step 4) and the key (the Key
object from 6). Recall from step 4 that we have
already placed the template Signature element at its correct place. A call to the
SignatureContext.sign()
method simply fills in the signature data in the
template.
Have a look at the XMLDSigSampleWithCertificatePointer
class of Listing 9, which is a very slightly modified
form of the XMLDSigSampleWithCertificate
class of Listing 7. The only difference between Listings 7 and 9 is in step 7.
Recall from the discussion on step 7 of Listing 7 that in order to set the binary content of a certificate into the
KeyInfo.X509Data
object, we called the setCertificate()
method
of the KeyInfo.X509Data
class. The result was that the binary data
representation of the certificate got wrapped inside the KeyInfo element.
But if, instead of the actual certificate, you wish to wrap a pointer to the certificate
inside the KeyInfo element, the KeyInfo.X509Data
class can still help you. Note
that a pointer refers to a certificate and the recipient of the message will map the
pointer
to the actual certificate before verifying the signature. The pointer to certificate
mapping
mechanism is not of our concern here. We are only interested in demonstrating how
to use
XSS4J to author XML digital signature messages that wrap pointers to X.509 certificates.
Please refer to the resources section to learn the details of X.509
certificates.
XSS4J supports the authoring of three types of certificate pointers, namely the issuer serial number, the subject name, and the subject ID. The issuer serial number is a name-value pair containing a name and serial number. The subject name is a string representing the subject of the certificate. The subject ID is an identifier that identifies the subject.
If you want to include a pointer to a certificate in your XMLDS message, you will
use the
setParameters()
method of the KeyInfo.X509Data
class (instead of
calling the setCertificate()
method), as shown in step 7 of Listing 9.
The setParameters()
method takes four parameters. The first parameter is a
certificate (the same X509Certificate
object that we instantiated in step 6).
The other three parameters are of Boolean type. You can pass on "true" as the value
of any
one or more of the three parameters.
If you pass true as the second parameter, the issuer name and serial number of the X509 certificate will be set inside the X509Data element. If you pass true as the third parameter, the subject ID will be set inside the X509Data element. If you pass true as the fourth parameter, the subject name will be set inside the X509Data element.
Notice that we have passed "true" as value of the second parameter to the
setParameters()
method in step 7 of Listing 9. The third and fourth parameters
are false. The resulting signed XML file is as shown in Listing 10.
If you want to include the subject identifier in the KeyInfo element, you will pass
true
as value of the third parameter to the setParameters()
method as shown below:
x5data.setParameters(cert, false, true, false);
The resulting XML file will be as shown in Listing 11.
If you want to include the subject name of the certificate in the KeyInfo element,
you
will pass "true" as value of the fourth parameter to the setParameters()
method
as shown below:
x5data.setParameters(cert, false, false, true);
The resulting signed XML file will be as shown in Listing 12.
You can also include multiple pointers to the same certificate in your singed XML
file.
For example, if you pass true as value of all three boolean types
(x5data.setParameters(cert, true, true, true)
), the resulting XML file will
appear as shown in Listing 13.
Now we will show how to author a KeyName element inside the KeyInfo to produce a signature.
The XMLDSigSampleWithKeyName
class of Listing 14 shows how to sign using the
KeyName element. Listing 14 is similar to Listing 7 except steps 7 and 8. This time
we are
not using the KeyInfo.X509Data
class so we don't need to do anything in step 7.
Therefore, step 7 is empty in Listing 14.
In step 8 we have used the setKeyNames()
method of the KeyInfo
class to author the KeyInfo element with a KeyName element.
The setKeyNames()
method takes an array of strings. Each string in the array
is a key name. As we have just one key name to wrap inside the KeyInfo element, so
we have
formed an array of just one key name and passed the array to the setKeyNames()
method.
The result of running XMLDSigSampleWithKeyName
is shown in Listing 15.
In this column we have learned how to use XSS4J to sign messages using certificates and keys. Next time, we will use these concepts to implement signature support in our WSS4J implementation.
Resources
|