XML Signature:
Implementations and Interoperability

Contact: Ed Simon, Entrust Technologies <ed.simon@entrust.com>

Note: This page summarizes a presentation given to the XML Signature work group at the 46th IETF Meeting in Washington DC, Nov 1999.

Implementations
How hard is the XML Signature specification to implement? Experience with implementing a spec is an excellent way of resolving some of the issues of implementability that are raised.
Interoperability
The success of the XML Signature specification will be aided by making it easy to ensure various vendors' implementations are interoperable.

Implementations Technology Demo

Entrust's XML Signature demo servlet was demonstrated. This section describes what the demo does.

Creating the XML Signature

When the servlet is started, it sends this page to the browser...


XML Signature Demo

Setting up the signature

This demo illustrates the creation and verification of XML Signatures. To create an XML Signature, do these steps:

  1. Specify the person whose private key is to be used for signing:
    Alice
    Bob
    Carl
  2. Specify the text to be signed (no hyphens please*):
  3. Click

* Hyphens are not allowed only because the specified text will be shown within a hyphen-delimited XML comment. Any hyphens specified will be replaced with underscores before any signing operations.


Viewing and verifying the XML Signature

Once the user selects the "Sign it!" button, the XML Signature is created and presented. The user may then choose to modify the XML Signature and/or select the public key that will be used when verifying the signature. (Note: The servlet outputs the <Signature> element into an HTML <TEXTAREA> element so that it can be edited.)


XML Signature Demo

Viewing and verifying the signature

Here's the XML Signature you just created:

<Signature xmlns="http://www.w3.org/Signature/core-19991020">

    <SignedInfo>
        <SignatureAlgorithm name="dsaWithSha-1"/>
        <ObjectReference>
            <Location uri="" idref="ID1"/>
            <!-- Digest of "When I grow up, I want to be an XML Signature!" -->
            <DigestValue algorithm="sha-1">
                ci/x61c2KGCB+1BedjUC0UgIE2Q=
            </DigestValue>
        </ObjectReference>
    </SignedInfo>

    <!-- Here's the canonicalized <SignedInfo> element: 
    "<n1:SignedInfo ...
         ...xmlns:n1="http://www.w3.org/Signature"><n1:SignatureAlgorithm ...
         ...xmlns:n1="http://www.w3.org/Signature" n2:name="dsaWithSha-1" ...
         ...xmlns:n2="http://www.w3.org/Signature"></n1:SignatureAlgorithm>...
         ...<n1:ObjectReference xmlns:n1="http://www.w3.org/Signature">...
         ...<n1:Location xmlns:n1="http://www.w3.org/Signature" n2:idref="ID1" ...
         ...xmlns:n2="http://www.w3.org/Signature" n3:uri="" ...
         ...xmlns:n3="http://www.w3.org/Signature"></n1:Location>...
         ...<n1:DigestValue xmlns:n1="http://www.w3.org/Signature" ...
         ...n2:algorithm="sha-1" xmlns:n2="http://www.w3.org/Signature">...
         ...ci/x61c2KGCB+1BedjUC0UgIE2Q= </n1:DigestValue>...
         ...</n1:ObjectReference></n1:SignedInfo>"
         -->
         
    <SignatureValue encoding= "base64">
        MCwCFGZmo1uobxogaY71PjhqnL4Dm4VYAhRdotEPi75hwfwD9SNBOHCZveTmOw==
    </SignatureValue>
    
    <!--Alice'skey-->
    <KeyInfo>
        <KeyValue encoding="base64">
             MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAd+SEOCxdBootQs1S533gOnoOCSkwf/IpdNLkQxIovx4+2n+iqMmUhBsEdoY95Ln0niU45yVXpLVKgrt/XiWg3NGhptIv7KNGvfgXClR+H1Es2wgPOsYDOENzHi//+p2RPEFVhMPd6i6p9TVvd/BGTXSYeVbh+E2LgMbcQ1RAUko=
        </KeyValue>
    </KeyInfo>
    
    <!--Text youspecified: "When I grow up, I want to be an XML Signature!" --> 
    <Object encoding="base64" id="ID1">
        V2hlbiBJIGdyb3cgdXAsIEkgd2FudCB0byBiZSBhbiBYTUwgU2lnbmF0dXJlIQ==         
    </Object>
    
</Signature>

To test the XML Signature verification code, follow these steps:

  1. [OPTIONAL] Try modifying the above content to see which changes break the signature and which do not. These types of alterations will break the signature:
    • Modifying a digest value (the value of <DigestValue> element). (Note: this may cause a digest processing exception.)
    • Modifying the value of the <SignatureValue> element.(Note: this may cause a signature processing exception.)
    • Adding an attribute or element within, and including, the<SignedInfo> element.
    • Changing the name of an attribute or element within, and including, the <SignedInfo> element.

    These types of alterations will NOT break the signature:

    • Adding whitespace before or after an element or attribute.
    • Adding an XML comment.
    • Changing the order of attributes within an element.
    • Adding an attribute or element external to the <SignedInfo> element. (Note: If the structure of the <Signature> element is modified too drastically, verification processing will fail!)

     

  2. [OPTIONAL] Select the person whose public key is to be used for verifying the signature:
    Alice
    Bob
    Carl

    Note: If you select someone other than Alice (who did the signing), the signature will NOT verify.

  3. To verify the signature, click

Implementations Technology Demo

Verification results -- Valid

When the user selects "Verify", the signature is validated. If each digest listed in the <SignedInfo> element is valid and if the <SignatureValue> element contains the correct signature for the <SignedInfo> element, then the signature as a whole is valid. If the signature is valid, the demo servlet would produce output like this:


XML Signature Demo

Verification parameters

Keys

Owner of signing private key: Alice
Owner of verification public key: Alice

Was the XML Signature edited?

Yes, modifications were made to the XML Signature. Certain types of modifications (as noted on the previous page) may affect the validity of the signature.

Signature verification results

The verification indicates that the signature is "VALID"


Verification results -- Invalid

If the signature is invalid, the servlet indicates why. The following output would be returned if the stated and calculated digests did not match.


XML Signature Demo

Verification parameters

Keys

Owner of signing private key: Alice
Owner of verification public key: Alice

Was the XML Signature edited?

Yes, modifications were made to the XML Signature. Certain types of modifications (as noted on the previous page) may affect the validity of the signature.

Signature verification results

The verification indicates that the signature is "NOT VALID: calculated and specified digests do NOT match!"


Implementation -- A peek at the code

This section contains code snippets illustrating the basic steps for creating and verifying an XML Signature.

Creating the signature

1. Create <Object> element

        String strObjectElement = "\n\t<!-- Text you specified: \"" + textToBeSigned + "\" -->\n" +
                                  "\t<Object encoding=\"base64\" id=\"ID1\">\n\t\t" +
                                  base64OfTextToBeSigned +
                                  "\n\t</Object>\n";

2. Digest the object and put digest and object reference in <SignedInfo> element

        try {
            MessageDigest msgdig = MessageDigest.getInstance("SHA");
                
            msgdig.update(bytes_textToBeSigned, 0, bytes_textToBeSigned.length);
            
            bytes_messageDigestOfTextToBeSigned = msgdig.digest();
            
        } catch (NoSuchAlgorithmException ex) {
            
            return "Error: NoSuchAlgorithmException: " + ex;
        }
            
        
        //    Get base64-encoded version of the digest bytes.
        
        String base64OfDigest = base64Encode(bytes_messageDigestOfTextToBeSigned);
        
        //  Create the <SignedInfo> element.
        String strSignedInfoElement =
                "\t<SignedInfo>\n" +
                "\t\t<SignatureAlgorithm name=\"dsaWithSha-1\"/>\n" +
                "\t\t<ObjectReference>\n" + 
                "\t\t\t<Location uri=\"\" idref=\"ID1\"/>\n" +
                "\t\t\t<!-- Digest of \"" + textToBeSigned + "\" -->" +
                "\n\t\t\t<DigestValue algorithm=\"sha-1\">\n\t\t\t\t" + 
                base64OfDigest + "\n\t\t\t</DigestValue>\n" +
                "\t\t</ObjectReference>\n" + 
                "\t</SignedInfo>\n";

3. Canonicalize the SignedInfo element

    
    private static String doW3cXmlCanonicalization2(Element element) {
        
        StringBuffer out = new StringBuffer("<n1:");
        
        out.append(element.getTagName());
        out.append(" xmlns:n1=\"http://www.w3.org/Signature\"");            
        
        NamedNodeMap nnmAttributes = element.getAttributes();
        
        // sort the attributes
        Vector vecSortedAttributes = new Vector();
        while (nnmAttributes.getLength() > 0) {
            
            String strLowestUtf8Name = nnmAttributes.item(0).getNodeName();
            
            for (int i = 0; i < nnmAttributes.getLength(); i ++) {
                
                String strCandidateUtf8Name = nnmAttributes.item(i).getNodeName();
                
                if (strCandidateUtf8Name.compareTo(strLowestUtf8Name) < 0) {
                    
                    strLowestUtf8Name = strCandidateUtf8Name;
                }
            }

            vecSortedAttributes.addElement(nnmAttributes.getNamedItem(strLowestUtf8Name));
            nnmAttributes.removeNamedItem(strLowestUtf8Name);
        }
        
        
        for (int i = 0; i < vecSortedAttributes.size(); i++) {
            
            Node node = (Node) vecSortedAttributes.elementAt(i);
            
            out.append(" n").append(i+2).append(":");
            out.append(node.getNodeName());
            out.append("=\"").append(node.getNodeValue());
            out.append("\" xmlns:n").append(i+2).append("=\"http://www.w3.org/Signature\"");            
        }
        
        out.append(">");
        
        
        NodeList nl = element.getChildNodes();
        
        for (int i = 0; i < nl.getLength(); i++) {
            
            Node node = nl.item(i);
            
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                
                out.append(doW3cXmlCanonicalization2((Element) node));
                
            } else if (node.getNodeType() == Node.TEXT_NODE) {
                
                out.append(node.getNodeValue().trim());

            } else if (node.getNodeType() == Node.CDATA_SECTION_NODE ) {
                
                out.append(node.getNodeValue().trim());
            }
        }
        
        out.append("</n1:" + element.getTagName() + ">");
        
        return out.toString();
    }

4. Sign the <SignedInfo> element and create the <SignatureValue> element

        KeyPair keypair...;
        
        //System.out.println("Signing the <SignedInfo> element...");        
        byte[] bytesSignatureValueOfSignedInfoElement = null;
try { Signature signature = Signature.getInstance("DSA"); signature.initSign(keypair.getPrivate()); signature.update(bytesCanonicalizedSignedInfoElement, 0,
bytesCanonicalizedSignedInfoElement.length); bytesSignatureValueOfSignedInfoElement = signature.sign(); } catch (Exception ex) { return "Error: Exception while signing SignedInfo element: " + ex; } // Get base64-encoded version of the signature value bytes. String base64OfSignatureValue = base64Encode(bytesSignatureValueOfSignedInfoElement); String strSignatureValueElement = "\n\t<SignatureValue encoding=\"base64\">\n\t\t" + base64OfSignatureValue + "\n\t</SignatureValue>\n";

5. Create <KeyInfo> element

        String strKeyInfoElement = "\n\t<!-- " + strSignerName + "'s key -->\n" +
                                   "\t<KeyInfo>\n\t\t<KeyValue encoding=\"base64\">\n\t\t\t" +
                                   base64PublicKey +
                                   "\n\t\t</KeyValue>\n\t</KeyInfo>\n";

6. Put elements of <Signature> together

        
        String strSignatureElement =
                "<Signature xmlns=\"http://www.w3.org/Signature/core-19991020\">\n\n" +
                strSignedInfoElement +
                "\n\t<!-- Here's the canonicalized <SignedInfo> element: \"" +
                strCanonicalizedSignedInfoElement + "\" -->\n\n\t" +
                strSignatureValueElement +
                strKeyInfoElement +
                strObjectElement +
                "\n</Signature>";

Verifying the signature

1. For each <ObjectReference> object, calculate its digest and compare it to the one stated in the <ObjectReference> element

        NodeList nlObjectReference =
                docXmlSignature.getElementsByTagName("ObjectReference");
        
        // Iterate through <ObjectReference> elements
        for (int i = 0; i < nlObjectReference.getLength(); i++) {
            
            Element elemObjectReference = (Element) nlObjectReference.item(i);
// Get the <Object> referred to by the <ObjectReference> element Element elemLocation = (Element) elemObjectReference.getElementsByTagName("Location").item(0); String locationIdref = elemLocation.getAttribute("idref").trim(); Element elemObject = getElementWithId(docXmlSignature.getDocumentElement(), locationIdref); if (elemObject == null) { return "Verification failed: Referenced object not found!"; } String strObjectContents = elemObject.getChildNodes().item(0).getNodeValue().trim(); byte[] bytesOfObjectElement = base64Decode(strObjectContents); // Create a SHA-1 message digest of the bytes. byte[] bytes_messageDigestOfObjectElement = null; try { MessageDigest msgdig = MessageDigest.getInstance("SHA"); msgdig.update(bytesOfObjectElement, 0, bytesOfObjectElement.length); bytes_messageDigestOfObjectElement = msgdig.digest(); } catch (NoSuchAlgorithmException ex) { System.out.println("NoSuchAlgorithmException: " + ex); } // Get the digest of the <Object> element. Is the same value as // <ObjectReference>-><DigestValue>. If not, validation fails. // Otherwise continue. String strCalculatedDigest = base64Encode(bytes_messageDigestOfObjectElement); String strDigestInSignedInfoElement = elemObjectReference.getElementsByTagName("DigestValue").item(0).getChildNodes().item(0).getNodeValue().trim(); if (!strCalculatedDigest.equals(strDigestInSignedInfoElement)) { return "NOT VALID: calculated and specified digests do NOT match!"; } } //System.out.println("Digests correlate, verifying <SignedInfo>...");

2. Canonicalize the <SignedInfo> element

...same code as used when signing...

3. Verify the signature on the canonicalized <SignedInfo> element

        
        try {
            Signature signature = Signature.getInstance("DSA");

            signature.initVerify(keypair.getPublic());

            signature.update(bytesCanonicalizedSignedInfoElement, 0,
bytesCanonicalizedSignedInfoElement.length); Element elemSignatureValue = (Element) docXmlSignature.getElementsByTagName("SignatureValue").item(0); String strSignatureValue = elemSignatureValue.getChildNodes().item(0).getNodeValue().trim(); byte[] bytesInputSignatureValue = base64Decode(strSignatureValue); boolValidityOfSignedInfoElement = signature.verify(bytesInputSignatureValue); } catch (Exception ex) { System.out.println("Exception while verifying <SignedInfo> element: " + ex); } if (!boolValidityOfSignedInfoElement) { return "NOT VALID: signature on <SignedInfo> element failed"; } return "VALID"; }

Interoperability

Testing an implementation's conformance to the XML Signature specification is divided into two phases. The first phase (REQUIRED) involves testing the implementation using the conformance test suite. The second phase (RECOMMENDED for implementations that are expected to operate with other vendor's implementations) involves each vendor setting up an autoresponder that attempts to verify the XML Signature object that it receives.

Conformance Test Suite

A test suite that exercises the REQUIRED, and perhaps also the RECOMMENDED, features of the XML Signature specification. Requires no inter-vendor effort.

XML Signature Auto-responder (XSAR)

A vendor's (XSAR) enables other vendors to test whether their XML Signature implementations are interoperable with the vendor's own XML Signature implementation. Normally, a vendor's XSAR would receive signed XML Signature objects, attempt to verify them, and return the result to the other vendor.

The XSAR concept is analogous to other autoresponder initiatives such as the S/MIME autoresponders provided by secure email vendors.