Instantiating the Document that Contains the Signature
Specifying the Signature Element to be Validated
Unmarshaling the XML Signature
What If the XML Signature Fails to Validate?
Instantiating the Document to be Signed
Printing or Displaying the Resulting Document
The Java XML Digital Signature API is a standard
Java API for generating and validating XML Signatures. This API was
defined under the Java Community Process as JSR 105 (see
http://jcp.org/en/jsr/detail?id=105
). This JSR
is final and this release of Java SE contains an FCS access
implementation of the Final version of the APIs.
XML Signatures can be applied to data of any type,
XML or binary (see http://www.w3.org/TR/xmldsig-core/
). The
resulting signature is represented in XML. An XML Signature can be
used to secure your data and provide data integrity, message
authentication, and signer authentication.
After providing a brief overview of XML Signatures and the XML Digital Signature API, this document presents two examples that demonstrate how to use the API to validate and generate an XML Signature. This document assumes that you have a basic knowledge of cryptography and digital signatures.
The API is designed to support all of the required or recommended features of the W3C Recommendation for XML-Signature Syntax and Processing. The API is extensible and pluggable and is based on the Java Cryptography Service Provider Architecture. The API is designed for two types of developers:
JCA
provider
The six packages listed below comprise the XML Digital Signature API:
The javax.xml.crypto
package contains common
classes that are used to perform XML cryptographic operations, such
as generating an XML signature or encrypting XML data. Two notable
classes in this package are the KeySelector
class, which allows developers to
supply implementations that locate and optionally validate keys
using the information contained in a KeyInfo
object, and the URIDereferencer
class, which allows developers
to create and specify their own URI dereferencing
implementations.
The javax.xml.crypto.dsig
package includes
interfaces that represent the core elements defined in the W3C XML
digital signature specification. Of primary significance is the
XMLSignature
class, which allows you to sign
and validate an XML digital signature. Most of the XML signature
structures or elements are represented by a corresponding interface
(except for the KeyInfo
structures,
which are included in their own package and are discussed in the
next paragraph). These interfaces include: SignedInfo
, CanonicalizationMethod
, SignatureMethod
, Reference
, Transform
, DigestMethod
, XMLObject
, Manifest
, SignatureProperty
, and SignatureProperties
. The XMLSignatureFactory
class is an abstract
factory that is used to create objects that implement these
interfaces.
The javax.xml.crypto.dsig.keyinfo
package
contains interfaces that represent most of the KeyInfo
structures defined in the W3C XML digital
signature recommendation, including KeyInfo
, KeyName
, KeyValue
, X509Data
, X509IssuerSerial
, RetrievalMethod
, and PGPData
. The KeyInfoFactory
class is an abstract
factory that is used to create objects that implement these
interfaces.
The javax.xml.crypto.dsig.spec
package
contains interfaces and classes representing input parameters for
the digest, signature, transform, or canonicalization algorithms
used in the processing of XML signatures.
Finally, the javax.xml.crypto.dom
and javax.xml.crypto.dsig.dom
packages
contains DOM-specific classes for the javax.xml.crypto
and javax.xml.crypto.dsig
packages,
respectively. Only developers and users who are creating or using a
DOM-based XMLSignatureFactory
or KeyInfoFactory
implementation will need
to make direct use of these packages.
A JSR 105 cryptographic service is a concrete
implementation of the abstract XMLSignatureFactory
and KeyInfoFactory
classes and is
responsible for creating objects and algorithms that parse,
generate and validate XML Signatures and KeyInfo
structures. A concrete implementation of
XMLSignatureFactory
must provide support for each of the required algorithms as specified by the W3C
recommendation for XML Signatures. It can optionally support other
algorithms as defined by the W3C recommendation or other
specifications.
JSR 105 leverages the JCA provider model for
registering and loading XMLSignatureFactory
and KeyInfoFactory
implementations.
Each concrete XMLSignatureFactory
or KeyInfoFactory
implementation supports a specific
XML mechanism type that identifies the XML processing mechanism
that an implementation uses internally to parse and generate XML
signature and KeyInfo
structures. This
JSR supports one standard type, DOM. The XML Digital Signature
provider implementation that is bundled with Java SE supports the
DOM mechanism. Support for new standard types, such as JDOM, may be
added in the future.
An XML Digital Signature API implementation
should use underlying JCA engine
classes, such as java.security.Signature
and java.security.MessageDigest
, to perform
cryptographic operations.
In addition to the XMLSignatureFactory
and KeyInfoFactory
classes, JSR 105 supports a service
provider interface for transform and canonicalization algorithms.
The TransformService
class allows you to
develop and plug in an implementation of a specific transform or
canonicalization algorithm for a particular XML mechanism type. The
TransformService
class uses the standard
JCA provider model for registering and loading implementations.
Each JSR 105 implementation should use
the TransformService
class to find a
provider that supports transform and canonicalization algorithms in
XML Signatures that it is generating or validating.
You can use an XML Signature to sign any arbitrary data, whether it is XML or binary. The data is identified via URIs in one or more Reference elements. XML Signatures are described in one or more of three forms: detached, enveloping, or enveloped. A detached signature is over data that is external, or outside of the signature element itself. Enveloping signatures are signatures over data that is inside the signature element, and an enveloped signature is a signature that is contained inside the data that it is signing.
The easiest way to describe the contents of an XML Signature is to show an actual sample and describe each component in more detail. The following is an example of an enveloped XML Signature generated over the contents of an XML document. The contents of the document before it is signed are:
The resulting enveloped XML Signature, indented and formatted for readability, is as follows:
<?xml version="1.0" encoding="UTF-8"?> <Envelope xmlns="urn:envelope"> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <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> </SignedInfo> <SignatureValue> KedJuTob5gtvYx9qM3k3gm7kbLBwVbEQRl26S2tmXjqNND7MRGtoew== </SignatureValue> <KeyInfo> <KeyValue> <DSAKeyValue> <P> /KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxe Eu0ImbzRMqzVDZkVG9xD7nN1kuFw== </P> <Q>li7dzDacuo67Jg7mtqEm2TRuOMU=</Q> <G> Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/ XPaF5Bpsy4pNWMOHCBiNU0NogpsQW5QvnlMpA== </G> <Y> qV38IqrWJG0V/mZQvRVi1OHw9Zj84nDC4jO8P0axi1gb6d+475yhMjSc/ BrIVC58W3ydbkK+Ri4OKbaRZlYeRA== </Y> </DSAKeyValue> </KeyValue> </KeyInfo> </Signature> </Envelope>
The Signature
element
has been inserted inside the content that it is signing, thereby
making it an enveloped signature. The required SignedInfo
element contains the information that is
actually signed:
<SignedInfo> <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> </SignedInfo>
The required CanonicalizationMethod
element defines the algorithm
used to canonicalize the SignedInfo
element before it is signed or validated. Canonicalization is the
process of converting XML content to a canonical form, to take into
account changes that can invalidate a signature over that data.
Canonicalization is necessary due to the nature of XML and the way
it is parsed by different processors and intermediaries, which can
change the data such that the signature is no longer valid but the
signed data is still logically equivalent.
The required SignatureMethod
element defines the digital
signature algorithm used to generate the signature, in this case
DSA with SHA-1.
One or more Reference
elements identify the data that is digested. Each Reference
element identifies the data via a URI. In
this example, the value of the URI is the empty String
(""), which indicates the root of the document. The
optional Transforms
element contains a
list of one or more Transform
elements,
each of which describes a transformation algorithm used to
transform the data before it is digested. In this example, there is
one Transform
element for the enveloped
transform algorithm. The enveloped transform is required for
enveloped signatures so that the signature element itself is
removed before calculating the signature value. The required
DigestMethod
element defines the
algorithm used to digest the data, in this case SHA1. Finally the
required DigestValue
element contains
the actual base64-encoded digested value.
The required SignatureValue
element contains the base64-encoded
signature value of the signature over the SignedInfo
element.
The optional KeyInfo
element contains information about the key that is needed to
validate the signature:
<KeyInfo> <KeyValue> <DSAKeyValue> <P> /KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxe Eu0ImbzRMqzVDZkVG9xD7nN1kuFw== </P> <Q>li7dzDacuo67Jg7mtqEm2TRuOMU=</Q> <G> Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/ XPaF5Bpsy4pNWMOHCBiNU0NogpsQW5QvnlMpA== </G> <Y> qV38IqrWJG0V/mZQvRVi1OHw9Zj84nDC4jO8P0axi1gb6d+475yhMjSc/ BrIVC58W3ydbkK+Ri4OKbaRZlYeRA== </Y> </DSAKeyValue> </KeyValue> </KeyInfo>
This KeyInfo
element
contains a KeyValue
element, which in
turn contains a DSAKeyValue
element
consisting of the public key needed to validate the signature.
KeyInfo
can contain various content such
as X.509 certificates and PGP key identifiers. See the KeyInfo section
of the XML Signature
Recommendation for more information on the different KeyInfo
types.
The following sections describe two examples that show how to use the XML Digital Signature API:
You can find the code shown in this section in the
Validate.java
file in the
docs/technotes/guides/security/xmldsig
directory.
The file on which it operates, envelopedSignature.xml
, is in the same
directory.
To compile and run the example, execute the
following commands from the docs/technotes/guides/security/xmldsig
directory:
$ javac Validate.java
$ java Validate signature.xml
signature.xml
in the current working directory.
This example shows you how to validate an XML Signature using the JSR 105 API. The example uses DOM (the Document Object Model) to parse an XML document containing a Signature element and a JSR 105 DOM implementation to validate the signature.
First we use a JAXP DocumentBuilderFactory
to parse the XML document
containing the Signature. An application obtains the default
implementation for DocumentBuilderFactory
by calling the following line
of code:
We must also make the factory namespace-aware:
Next, we use the factory to get an instance of a
DocumentBuilder
, which is used to parse
the document:
DocumentBuilder builder = dbf.newDocumentBuilder(); Document doc = builder.parse(new FileInputStream(argv[0]));
We need to specify the Signature
element that we want to validate, since
there could be more than one in the document. We use the DOM method
Document.getElementsByTagNameNS
, passing
it the XML Signature namespace URI and the tag name of the
Signature
element, as shown:
NodeList nl = doc.getElementsByTagNameNS (XMLSignature.XMLNS, "Signature"); if (nl.getLength() == 0) { throw new Exception("Cannot find Signature element"); }
This returns a list of all Signature
elements in the document. In this example,
there is only one Signature
element.
We create an XMLValidateContext
instance containing input
parameters for validating the signature. Since we are using DOM, we
instantiate a DOMValidateContext
instance (a subclass of XMLValidateContext
), and pass it two parameters, a
KeyValueKeySelector
object and a
reference to the Signature
element to be
validated (which is the first entry of the NodeList
we generated earlier):
The KeyValueKeySelector
is explained in greater detail in Using
KeySelectors.
We extract the contents of the Signature
element into an XMLSignature
object. This process is called
unmarshalling. The Signature
element is
unmarshalled using an XMLSignatureFactory
object. An application can
obtain a DOM implementation of XMLSignatureFactory
by calling the following line of
code:
We then invoke the unmarshalXMLSignature
method of the factory to
unmarshal an XMLSignature
object, and
pass it the validation context we created earlier:
Now we are ready to validate the signature. We do
this by invoking the validate
method on
the XMLSignature
object, and pass it the
validation context as follows:
The validate
method
returns "true" if the signature validates successfully
according to the core validation rules
in the W3C XML Signature Recommendation
,
and false otherwise.
If the XMLSignature.validate
method returns false, we can
try to narrow down the cause of the failure. There are two phases
in core XML Signature validation:
Each phase must be successful for the signature to be valid. To check if the signature failed to cryptographically validate, we can check the status, as follows:
boolean sv = signature.getSignatureValue().validate(valContext); System.out.println("signature validation status: " + sv);
We can also iterate over the references and check the validation status of each one, as follows:
Iterator i = signature.getSignedInfo().getReferences().iterator(); for (int j=0; i.hasNext(); j++) { boolean refValid = ((Reference) i.next()).validate(valContext); System.out.println("ref["+j+"] validity status: " + refValid); }
KeySelectors
are used
to find and select keys that are needed to validate an
XMLSignature. Earlier, when we created a DOMValidateContext
object, we passed a KeySelector
object as the first argument:
Alternatively, we could have passed a PublicKey
as the first argument if we already knew
what key is needed to validate the signature. However, we often
don't know.
The KeyValueKeySelector
is a concrete implementation of the abstract KeySelector
class. The KeyValueKeySelector
implementation tries to find an
appropriate validation key using the data contained in KeyValue
elements of the KeyInfo
element of an XMLSignature
. It does not determine if the key is
trusted. This is a very simple KeySelector
implementation, designed for
illustration rather than real-world usage. A more practical example
of a KeySelector
is one that searches a
KeyStore
for trusted keys that match
X509Data
information (for example,
X509SubjectName
, X509IssuerSerial
, X509SKI
, or X509Certificate
elements) contained in a
KeyInfo
.
The implementation of the KeyValueKeySelector
is as follows:
private static class KeyValueKeySelector extends KeySelector { public KeySelectorResult select(KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method, XMLCryptoContext context) throws KeySelectorException { if (keyInfo == null) { throw new KeySelectorException("Null KeyInfo object!"); } SignatureMethod sm = (SignatureMethod) method; List list = keyInfo.getContent(); for (int i = 0; i < list.size(); i++) { XMLStructure xmlStructure = (XMLStructure) list.get(i); if (xmlStructure instanceof KeyValue) { PublicKey pk = null; try { pk = ((KeyValue)xmlStructure).getPublicKey(); } catch (KeyException ke) { throw new KeySelectorException(ke); } // make sure algorithm is compatible with method if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) { return new SimpleKeySelectorResult(pk); } } } throw new KeySelectorException("No KeyValue element found!"); } static boolean algEquals(String algURI, String algName) { if (algName.equalsIgnoreCase("DSA") && algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) { return true; } else if (algName.equalsIgnoreCase("RSA") && algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) { return true; } else { return false; } } }
The code discussed in this section is in the
GenEnveloped.java
file in the
docs/technotes/guides/security/xmldsig
directory. The
file on which it operates, envelope.xml
,
is in the same directory. It generates the file envelopedSignature.xml.
To compile and run this sample, execute the
following command from the
docs/technotes/guides/security/xmldsig
directory:
$ javac GenEnveloped.java
$ java GenEnveloped envelope.xml
envelopedSignature.xml
The sample program will generate an enveloped signature of the
document in the file envelope.xml and store it in the file
envelopedSignature.xml
in the current working
directory.
This example shows you how to generate an XML Signature using the XML Digital Signature API. More specifically, the example generates an enveloped XML Signature of an XML document. An enveloped signature is a signature that is contained inside the content that it is signing. The example uses DOM (the Document Object Model) to parse the XML document to be signed and a JSR 105 DOM implementation to generate the resulting signature.
A basic knowledge of XML Signatures and their
different components is helpful for understanding this section. See
http://www.w3.org/TR/xmldsig-core/
for more
information.
First, we use a JAXP DocumentBuilderFactory
to parse the XML document
that we want to sign. An application obtains the default
implementation for DocumentBuilderFactory
by calling the following line
of code:
We must also make the factory namespace-aware:
Next, we use the factory to get an instance of a
DocumentBuilder
, which is used to parse
the document:
DocumentBuilder builder = dbf.newDocumentBuilder(); Document doc = builder.parse(new FileInputStream(argv[0]));
We generate a public key pair. Later in the
example, we will use the private key to generate the signature. We
create the key pair with a KeyPairGenerator
. In this example, we will create a
DSA KeyPair
with a length of 512 bytes
:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA"); kpg.initialize(512); KeyPair kp = kpg.generateKeyPair();
In practice, the private key is usually previously
generated and stored in a KeyStore
file
with an associated public key certificate.
We create an XML Digital Signature XMLSignContext
containing input parameters for
generating the signature. Since we are using DOM, we instantiate a
DOMSignContext
(a subclass of
XMLSignContext
), and pass it two
parameters, the private key that will be used to sign the document
and the root of the document to be signed:
We assemble the different parts of the
Signature
element into an XMLSignature
object. These objects are all created
and assembled using an XMLSignatureFactory
object. An application obtains a
DOM implementation of XMLSignatureFactory
by calling the following line of
code:
We then invoke various factory methods to create
the different parts of the XMLSignature
object as shown below. We create a Reference
object, passing to it the following:
Next, we create the SignedInfo
object, which is the object that is
actually signed, as shown below. When creating the SignedInfo
, we pass as parameters:
Next, we create the optional KeyInfo
object, which contains information that
enables the recipient to find the key needed to validate the
signature. In this example, we add a KeyValue
object containing the public key. To create
KeyInfo
and its various subtypes, we use
a KeyInfoFactory
object, which can be
obtained by invoking the getKeyInfoFactory
method of the XMLSignatureFactory
, as follows:
We then use the KeyInfoFactory
to create the KeyValue
object and add it to a KeyInfo
object:
KeyValue kv = kif.newKeyValue(kp.getPublic()); KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
Finally, we create the XMLSignature
object, passing as parameters the
SignedInfo
and KeyInfo
objects that we created earlier:
Notice that we haven't actually generated the signature yet; we'll do that in the next step.
Now we are ready to generate the signature, which
we do by invoking the sign
method on the
XMLSignature
object, and pass it the
signing context as follows:
The resulting document now contains a signature, which has been inserted as the last child element of the root element.
You can use the following code to print the resulting signed document to a file or standard output:
OutputStream os; if (args.length > 1) { os = new FileOutputStream(args[1]); } else { os = System.out; } TransformerFactory tf = TransformerFactory.newInstance(); Transformer trans = tf.newTransformer(); trans.transform(new DOMSource(doc), new StreamResult(os));