XML digital signatures have been on programmers' wish lists for some time. The good news is that JSR 105 now defines a standard Java technology API for providing XML digital signatures. This API is now part of the Java Web Services Development Pack (Java WSDP) 1.5. The Java WSDP 1.5 includes an implementation of the JSR 105 Proposed Final Draft. (Note that this is only the "proposed" final draft. The libraries might change before the final release.) The stated purpose of the Java XML Digital Signature API is to provide a vendor-neutral implementation of the W3C Recommendations for XML-Signature Syntax and Processing.
What is a digital signature? Much like its pen and paper counterpart, a digital signature assures to anyone reading data that the author is indeed who he or she claims to be. A digital signature provides what cryptographers call "authentication." It also ensures that the content of the data is exactly the same as what the author signed (that is, nothing has been added or removed). In the world of cryptography, this is known as "integrity."
Performing a digital signature involves two steps. In the first step, the data is run through a hashing algorithm. A typical hashing algorithm scans through the data and generates a number of some size -- this is typically called a "digest." If the same data is run through the hashing algorithm again, the same digest should be generated. Good hashing algorithms vary the digest unpredictably if the slightest change is made in the data. This makes it impossible to reverse engineer the original data, given the digest.
The second step in producing a digital signature is to encrypt the digest using the private key of the author. If you're not familiar with the terms public key or private key, then you've probably never heard of public key cryptography. The basic concepts of public key cryptography are simple: anything that is encrypted using an individual's private key, can only be decrypted using the same individual's public key. The reverse is also true: anything encrypted using an individual's public key, can only be decrypted using the same individual's private key. The two keys are mathematically linked. After encrypting the digest with the user's private key, the resulting scrambled data is then appended to the original document data.
Because public keys can be shared with anyone, and private keys should be known only to the signing author, verifying a digital signature is simple. The steps are:
- Rehash the document data that was received.
- Decrypt the encrypted digest with the author's public key that is typically appended to the document.
- Compare the two digests. If they are equal, the signature is valid.
If the two digests are not equal, then either the document has been altered, or the author of the document is not the same as the individual that signed it. However this information does not indicate which of those two faults (or both) have occurred.
The Java XML Digital Signature API
There are two parts of the JSR 105 API. The first part allows Java developers to create XML digital signatures for their data. The second part allows third-party developers to create provider implementations of the XML Digital Signature API and register them. This tip covers the first part only. It presents a simple example of how to sign an XML document and then verify that signature. Note that the XML signatures generated by JSR 105 can be applied to both XML and binary data. The resulting signature is created in XML.
An XML Signature takes one of three forms. Assuming that the XML signature is contained in a <Signature>
element, the only difference between the three forms is where the <Signature>
element is located with respect to the document data. The three forms are:
- Detached. A detached signature is over data that is external to the signature element. That is:
<Signature>
</Signature>
<DocumentData>
</DocumentData> - Enveloping. An enveloping signature is a signature over data that is inside the signature element.
<Signature>
<DocumentData>
</DocumentData>
</Signature> - Enveloped. An enveloped signature is a signature that is contained inside the data that it is signing.
<DocumentData>
<Signature>
</Signature>
</DocumentData>
Let's assume that the data to be signed is contained in an <Envelope>
element. Here is what the XML document looks like before applying a signature:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="urn:envelope">
<!--data is here-->
</Envelope>
Here's what the XML document looks like after the data is signed with an enveloped signature (note that these examples have been re-formatted and indented for easier reading):
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<!--data is here-->
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<!-- see below for the contents of this element -->
</SignedInfo>
<SignatureValue>
<!-- see below for the contents of this element -->
</SignatureValue>
<KeyInfo>
<!-- see below for the contents of this element -->
</KeyInfo>
</Signature>
</Envelope>
A number of elements are added inside of the <Signature>
element by the XML digital signature process. These elements are <SignedInfo>
,<SignatureValue>
element, and <KeyInfo>
.
The <SignedInfo>
element contains signature information, as well as references to the data that is to be signed. In actuality, <SignedInfo>
is the element that the signature is calculated over. Here is how this happens:
- The data that the references point to is transformed, canonicalized (see below) and digested.
- The
<SignedInfo>
element containing those references is canonicalized, digested and signed.
Here is what a <SignedInfo>
element might look like:
<CanonicalizationMethod Algorithm=
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
<SignatureMethod Algorithm=
"http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm=
"http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm=
"http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>uooqbWYa5VCqcJCbuymBKqm17vY=</DigestValue>
</Reference>
The elements within the <SignedInfo>
element are:
<CanonicalizationMethod>
. Describes the algorithm that the signature process used to generate a canonical version of theSignedInfo
element. A canonical version of an XML document resolves trivial variances from different XML generators, such as explicit namespace-scoped elements or whitespaces, that would generate different signatures for documents that are otherwise logically equivalent. For more information on why canonicalization is necessary, see the W3C document Canonical XML Version 1.0.
<SignatureMethod>
. Identifies the method used to generate the signature over the<SignedInfo>
element. In this example, the DSA (Digital Signature Algorithm) is used after theSignedInfo
digest is calculated with the SHA (Secure Hashing Algorithm) 1.
<Reference>
-- One or more<Reference>
elements are listed that scope the data affected by their child elements. In the example, the URI attribute contains an empty string (""), that refers to the root element of the XML document (and so includes the entire document). First a transformation is performed on the data to ensure that the enveloped signature is not included in any subsequent calculations. Next, the data is digested using the digest algorithm listed. Finally, the digest value that was generated for the document by that algorithm is shown.
The second element, <SignatureValue>
, is relatively simple. It contains an arbitrarily-large text-encoded number which is the scrambled signature for the document in question. For example:
KedJuTob5gtvYx9qM3k3gm7kbLBwVbEQRl26S2tmXjqNND7MRGtoew==
Anyone who receives the XML document must be able to validate this signature. So the original document author (the signer) must append his or her public key to the document. (In reality, the public key should be authenticated with a certificate authority, but that's beyond the scope of this tip.) The public key is stored in the third element, <KeyInfo>
. Here is an example of a raw DSA key. The value is in the <KeyValue>
element (a subelement of <KeyInfo>
):
<KeyValue>
<DSAKeyValue>
<P>
/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxe
Eu0ImbzRMqzVDZkVG9xD7nN1kuFw==
</P>
<Q>li7dzDacuo67Jg7mtqEm2TRuOMU=</Q>
<G>Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/
XPaF5Bpsy4pNWMOHCBiNU0NogpsQW5QvnlMpA==
</G>
<Y>qV38IqrWJG0V/
mZQvRVi1OHw9Zj84nDC4jO8P0axi1gb6d+475yhMjSc/
BrIVC58W3ydbkK+Ri4OKbaRZlYeRA==
</Y>
</DSAKeyValue>
</KeyValue>
Using the XML Digital Signature API
Here is an example that can be used to generate an XML digital signature (the example is adapted from the examples provided for the API with Java WSDP 1.5):
String providerName = System.getProperty("jsr105Provider", |
The following lines of code create the XML signature factory, which is used to sign the document:
String providerName = System.getProperty("jsr105Provider", |
The code then creates Reference
and SignedInfo
objects. These map directly to their respective elements in the XML signature.
Reference ref = |
Next, the JCA classes are used to generate a 512-bit DSA key, and create a KeyInfo
object that includes the public key. The KeyInfo
object also maps to the <KeyInfo>
element in the signature.
KeyPairGenerator kpg = |
Finally, the code uses the JAXP DocumentBuilderFactory
to create a document from the filename given, create an XML signature object, and use the TransformerFactory
to write the signature to an OutputStream
. The result is sent to the file mySignedFile
.
DocumentBuilderFactory dbf = |
Validating the Signature
Here is the source code needed to validate the signature:
DocumentBuilderFactory dbf = |
The first section of code uses the JAXP DocumentBuilderFactory
to create a namespace-aware Document
object from the file mySignedFile
:
DocumentBuilderFactory dbf = |
Next, it programmatically extracts the <Signature>
element (and all its children) from the XML:
NodeList nl = |
Then it creates an XMLSignatureFactory
and DOMValidateContext
object for use in unmarshalling the XML signature:
String providerName = System.getProperty( |
After an appropriate XMLSignature
object is obtained, the program calls the validate()
method using the appropriate context to determine whether the signature is valid:
XMLSignature signature = |
For more information about the Digital Signature API, see Chapter 4: Java XML Digital Signature API in the Java Web Services Tutorial.
Running the Sample Code for the Java XML Digital Signature API Tip
- Download the sample archive for the Java XML Digital Signature API tip.
- Download and install Java WSDP 1.5 from the Java Web Services Developer Pack Downloads page.
- Set your executable
PATH
to include theAnt
application, which is located in theapache-ant/bin
directory of Java WSDP 1.5. For example, if you installed Java WSDP 1.5 inC:/jwsdp-1.5
in the Windows environment, enter the commands:
set JWSDP_HOME=C:/jwsdp-1.5
set ANT_HOME=%JWSDP_HOME%/apache-ant
set PATH=%ANT_HOME%/bin;%PATH% - Change to the directory where you downloaded the sample archive. Uncompress the JAR file for the sample archive as follows:
jar xvf ttJan2005xmldsig.jar
- Change to the
xmldsig
base directory. Edit theAnt
script (build.xml
) to point to the base directory of your Java WSDP 1.5 installation.
- Run the
build.xml
Ant
script by entering:
ant
on the command line.
In response, you should see the following:
Buildfile: build.xml
compile:
run:
[java] Generating DSA Key Pair...
[java] Signing XML Document...
[java] Signed XML Output Save to File:
envelopedSignature.xml
[java] Retrieving Signed XML File At:
envelopedSignature.xml
[java] Searching for Element...
[java] Verifying Signature...
[java] Signature Is Valid
BUILD SUCCESSFUL
Copyright (c) 2004-2005 Sun Microsystems, Inc.
All Rights Reserved.
Related Tips
- Accessing a Secure Enterprise Bean From a Java Client or Through Java Web Start Technology
- Accessing an EJB from an applet
- Accessing bean components from JSP
- Accessing EJB from a servlet within the same container
- An example of a simple JSP page
- Automated code generation
- Automated update of EJB deployment descriptor
- Bean with Indexed Properties and Accessing Indexed values through JSP Bean tags