Signing Messages with XSS4J
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.
As you may recall from the third column, implementing a particular token needs two steps:
encrypt() method of the WSSMessage 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:
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
the KeyStoreKeyInfoResolver object.
ReferenceListOnlyToken
constructor authors the name of the encryption key as contents of the
KeyName element.
EncryptedKeyToken.encrypt() method in Listing
5 of the previous column, except that Listing 1 does not use
the KeyStoreKeyInfoResolver object. Recall that step 6 of
the encrypt() method (Listing
5 of the third column) made a call to
the EncryptionContext.setKeyInfoResolver() method. Now
in Listing 1,
the encrypt() method of
the ReferenceListOnlyToken class makes a call to
the EncryptionContext.setKey() method. This is because we
are not using the KeyStoreKeyInfoResolver class and
therefore we need to provide the key directly to
the EncryptionContext class.
getXMLString() method in Listing 1 authors the actual token
(a ReferenceList element, as shown in Listing
4 of the previous column). Notice that
the getXMLString() method of Listing 1 authors the token from
scratch. While in the EncryptedKeyToken.getXMLString()
method (Listing
5 of the previous column), the token was part of the encryption
template, and we extracted the token from
the EncryptedData element in step 7 of
the encrypt() method. You can see that authoring
a ReferenceListOnlyToken is simpler than
authoring the EncryptedKeyToken.
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:
WSSMessage object.
Token implementation and call its setSecret() method.
encryptElement() method of
the WSSMessage object that you created in step 1.
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:
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
|
XML.com Copyright © 1998-2006 O'Reilly Media, Inc.