XML Signature (also called XMLDSig, XML-DSig, XML-Sig) defines an XML syntax for digital signatures and is defined in the W3C
recommendation XML Signature Syntax and Processing. Functionally, it has much in common with PKCS#7
but is more extensible
and geared towards signing XML documents. It is used by various Web technologies such as SOAP, SAML, and others.
XML signatures can be used to sign data–a resource–of any type, typically XML documents, but anything that is accessible via a URL can be signed. An XML signature used to sign a resource outside its containing XML document is called a detached signature; if it is used to sign some part of its containing document, it is called an enveloped signature; if it contains the signed data within itself it is called an enveloping signature.
XML-DSig provides three methods for signing XML documents depending of the Signature required scope:
- Ax.crypt.XMLDSigEnveloped: Allows to sign XML Documents in Enveloped mode
- Ax.crypt.XMLDSigEnveloping: Allows to sign XML Documents in Enveloping mode
- Ax.crypt.XMLDSigDetached: Allows to sign XML Documents in Detached mode
All this methods shares common configuration methods to define digest, signature or canonicalization modes.
1 Configure XML-DSig
You can configure a few parameters before signing a document.
1.1 Select Digest method
You can select any of the available methods (default is SHA256):
- SHA1
- SHA224
- SHA256
- SHA384
- SHA512
- RIPEMD160
- SHA3_224
- SHA3_256
- SHA3_384
- SHA3_512
var dbf = new Ax.xml.DocumentBuilderFactory(); dbf.setNamespaceAware(true); var xml = dbf.parse("<rootxmldoc><tag1 /></rootxmldoc>"); var dst = new Ax.crypt.XMLDSigEnveloped(xml); console.log(dst.getDigestMethod()); dst.setDigestMethod("SHA512");
1.2 Select Signature method
You can select any of the available methods (default is RSA_SHA256):
- DSA_SHA1
- DSA_SHA256
- ECDSA_SHA1
- ECDSA_SHA1224
- ECDSA_SHA256
- ECDSA_SHA384
- ECDSA_SHA512
- HMAC_SHA1
- HMAC_SHA224
- HMAC_SHA384
- HMAC_SHA512
- RSA_SHA1
- RSA_SHA224
- RSA_SHA256
- RSA_SHA384
- RSA_SHA512
- SHA1_RSA_MGF1
- SHA224_RSA_MGF1
- SHA224_RSA_MGF1
- SHA384_RSA_MGF1
- SHA512_RSA_MGF1
Signature method is usualy based on the type of key. If you use a RSA key, signature method should be a RSA method. If you use a DSA key, signature method should be a DSA method.
var dbf = new Ax.xml.DocumentBuilderFactory(); dbf.setNamespaceAware(true); var xml = dbf.parse("<rootxmldoc><tag1 /></rootxmldoc>"); var dst = new Ax.crypt.XMLDSigEnveloped(xml); console.log("Digest Method ...: " + dst.getDigestMethod()); dst.setDigestMethod("SHA512"); console.log("Signature Method : " + dst.getSignatureMethod()); dst.setSignatureMethod("RSA_SHA1");
1.3 Select Canonicalization method
CanonicalizationMethod (transform) method can selected by using one of the following constants (default is ENVELOPED).
ENVELOPED | http://www.w3.org/2000/09/xmldsig#enveloped-signature |
INCLUSIVE | http://www.w3.org/TR/2001/REC-xml-c14n-20010315 |
INCLUSIVE_WITH_COMMENTS | http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments |
EXCLUSIVE | http://www.w3.org/2001/10/xml-exc-c14n# |
EXCLUSIVE_WITH_COMMENTS | http://www.w3.org/2001/10/xml-exc-c14n#WithComments |
BASE64 | http://www.w3.org/2000/09/xmldsig#base64 |
XPATH | http://www.w3.org/TR/1999/REC-xpath-19991116 |
XPATH2 | http://www.w3.org/2002/06/xmldsig-filter2 |
Inclusive Canonicalization copies all the declarations, even if they are defined outside of the scope of the signature. In this way all the declarations you might use will be unambiguously specified. A problem appears when the signed XML is moved into another XML document which has other declarations because the Inclusive Canonicalization will copy then and the signature will be invalid.
Exclusive Canonicalization finds out what namespaces you are actually using (the ones that are a part of the XML syntax) and just copies those. It does not look into attribute values or element content, so the namespace declarations required to process these are not copied.
This type of canonicalization is useful when you have a signed XML document that you wish to insert into other XML documents and it will insure the signature verifies correctly every time, so it is required when you need self-signed structures that support placement within different XML contexts.
Inclusive Canonicalization is useful when it is less likely that the signed data will be inserted in other XML document and it's the safer method from the security perspective because it requires no knowledge of the data that are to be secured in order to safely sign them.
var dbf = new Ax.xml.DocumentBuilderFactory(); dbf.setNamespaceAware(true); var xml = dbf.parse("<rootxmldoc><tag1 /></rootxmldoc>"); var dst = new Ax.crypt.XMLDSigEnveloped(xml); dst.setDigestMethod("SHA512"); dst.setSignatureMethod("RSA_SHA1"); dst.setCanonicalizationMethod("ENVELOPED"); console.log("Digest Method ...: " + dst.getDigestMethod()); console.log("Signature Method : " + dst.getSignatureMethod());
1.4 Signature Namespace
setSignatureNamespace(String) allows to define the name of namespace for signature tag generated by XMLDsig.
// Load a keystore var ks = new Ax.ks.KeyStoreManager("https://bitbucket.org/deister/axional-docs-resources/raw/master/KeyStores/swview/jks-files/jack.jks", "secret"); // Generate an XML Document by parsing a string var dbf = new Ax.xml.DocumentBuilderFactory(); dbf.setNamespaceAware(true); var xml = dbf.parse("<rootxmldoc><tag1 /></rootxmldoc>"); var dst = new Ax.crypt.XMLDSigEnveloped(xml); dst.setDigestMethod("SHA512"); dst.setSignatureMethod("RSA_SHA1"); // Namespace used to create Signature tags dst.setSignatureNamespace("ds"); console.log("Digest Method ...: " + dst.getDigestMethod()); console.log("Signature Method : " + dst.getSignatureMethod()); // Sign the document using keystore provate key alias "jack" with password "moon" console.log(dst.sign(ks, "jack", "moon"));
2 Enveloped Signature
This is the most common method used to sign XML documents. The signature element is embeded into the xml document and can be located into any node. Usualy signature tag is created into the root element, but can be located into other inner elements.
The signature is composed of two elements: signedinfo and signature value
In the signature process, first the system gets the elements in the XML document pointed by the reference URIs. This reference URIs can point to the whole document if defined as a void string (""), or to a node you can select using ID or xpath. After getting the node pointed by each reference URI, the signature procedure hash its contents using the method declared in the "digest method" parameter and creates entries like:
<Reference URI="#bk101"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>dEz1781zwlLiOd3kWbikuxN5HDq3KcdW2j3J1DllMDY=</DigestValue></Reference> <Reference URI="#bk102"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>wfBlEDAx3v/xIHIfhUZgujZZiNy9vOTXRhUEsg+C/TQ=</DigestValue></Reference>
You can add as many references to document elements as you want, and the procedure computes hash for each of this elements. After signature you'll be able to change any document element not referenced in a URI and the signature will be still correct.
After the hash computing, and using the "signature method" and the certificate provided, the signature procedure computes the signature data of signing the signed info signature element containing the hashes from the original document.
2.1 Examples
The following example signs an XML document.
// Load a keystore var ks = new Ax.ks.KeyStoreManager("https://bitbucket.org/deister/axional-docs-resources/raw/master/KeyStores/swview/jks-files/jack.jks", "secret"); // Load an XML document var src = new Ax.xml.XMLDocument(new Ax.net.URL('https://bitbucket.org/deister/axional-docs-resources/raw/master/XML/books.xml')); // Instantiate Enveloped signature passing the source document as parameter var dsig = new Ax.crypt.XMLDSigEnveloped(src); // Define the node name where signature block will be inserted // "" inserts signature into the root tag (default) dsig.setSignatureTagName("book"); // Set the reference names into the document to hash and create signature // If not declared whole document is hashed and signed dsig.setReferenceNames(["#bk101", "#bk102"]); console.log(" DigestMethod:" + dsig.getDigestMethod()); console.log("SignatureMethod:" + dsig.getSignatureMethod()); // Sign the document using keystore provate key alias "jack" with password "moon" var tmp = dsig.sign(ks, "jack", "moon"); console.log(tmp);
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="#bk101"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>dEz1781zwlLiOd3kWbikuxN5HDq3KcdW2j3J1DllMDY=</DigestValue></Reference>
<Reference URI="#bk102"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><DigestValue>wfBlEDAx3v/xIHIfhUZgujZZiNy9vOTXRhUEsg+C/TQ=</DigestValue></Reference>
</SignedInfo>
<SignatureValue>Ztsdj7Z0xkNgQ/k0jru+0ALKz+kWcpzOCU5ExDpDwtrKUtupOBwYmjCjGnDArvK6HaZFJu6JpgiE
XuysPWlnGGc+7T0rio4B88UyMGEXH+BT4uwrSgJetAr2CqrCsmhmacul8yVmCwmlKhCvvn970b65
8OTjI+grtj0Di5Lvdkc=</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>qAIsXru2kWzNXidrgyapDb7GdmhUwNFx1rOimDyu2RrJN9sIv0Zi2B0Kp1xSQiBPWabXbtt3wB1L
zS2P19tMC+MW7BTYz0mRg4n9vSoa+mTJ3Ea6/v4W97a701BSEOlTxysVltqgO+D3gD9uNVpjiCNj
XP3FlXrw44aDnXwme3s=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue></KeyInfo></Signature></book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
</book>
<book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology
society in England, the young survivors lay the
foundation for a new society.</description>
</book>
....
</catalog>
3 Enveloping Signature
The signature element is created as the root (document) element and the XML document is inserted as a child element into a <ds:Object> element. Reference URI of the signature points to this <ds:Object> element, so all it's content is being hashed and protected by the signature.
For example, the following signature template is for an enveloping signature in which a text object containing the plain text "Hello, World!" is embedded.
<?xml version="1.0" encoding="UTF-8"?> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <ds:Reference URI="#obj"> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue/> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue/> <ds:Object Id="obj">Hello, World!</ds:Object> </ds:Signature>
Notice that the reference of the text object uses the value of the Id attribute ("obj").