Verifying Lotus Forms XML digital signatures with Java

IBM® Lotus® Forms 3.5 provides support for XML digital signatures (XMLDSig) from W3C XML Signature Syntax and Processing. An XML digital signature for a Lotus Forms document can cover markup for the presentation layer and XML data managed by the XForms markup within the Lotus Forms document. This ability enables the signature to protect the full human context of a contract or agreement between the signing parties.

Eric Fu (ericfu@ca.ibm.com), Software Developer, IBM  

Eric Fu is a software developer at the IBM Victoria Lab. He has a Master’s degree in computer science from University of Ottawa, and currently he works on Lotus Forms Server API. You can reach him at ericfu@ca.ibm.com.



John Boyer, Ph.D (boyerj@ca.ibm.com), Senior Technical Staff Member, IBM  

John Boyer is a senior technical staff member at the IBM Victoria Lab. He has a Ph.D. in computer science from the University of Victoria and currently works as a software architect and researcher in IBM Lotus.


developerWorks Contributing author
        level

14 July 2009

Also available in Chinese

Editor's note: Know a lot about this topic? Want to share your expertise? Participate in the IBM Lotus software wiki program today.

Introduction of Lotus Forms XML digital signatures

This article focuses on verification of a signed Lotus Forms document with JSR 105 API without dependency on the Lotus Forms API (source code is available for download, see Download). This approach makes it easy for products acquired from other vendors to integrate with Lotus Forms using standard Java™ API calls and XML signature implementations acquired from other vendors.

In the context of this article, a form is an XML document that contains markup described in Extensible Forms Description Language (XFDL), which is a vocabulary in standard XML format used to describe Lotus Forms documents. In an XFDL form, user interface controls are called items, which are encoded using XML elements and attributes. Items usually bind to data in XForms instances, and they are organized into one or more visual pages.

XML signatures are contained in the XFDL document, so they are enveloped signatures. In accordance with the Model-View-Controller (MVC) concept, an XML digital signature belongs to part of the data Model in the XForms instance because the generation of a digital signature is an act of information creation by a user with a private key. After the signature is generated, any changes to the XFDL document can make a signature fail to verify. An XML signature can also selectively omit parts of a document being signed so that a multistep workflow can perform operations on those omitted parts without disturbing the validity of the digital signature. Two benefits of such an arrangement are that multiple signatures are allowed in a form and that signatures can overlap each other.

Listing 1 is an example of the XML signature in an XForms model. Note that the namespace URI for xforms, ds, and dsxp are defined in the document element <XFDL> of the form and is not shown here. A complete example of the form can be found in Download section.

Listing 1. An XForms instance containing an unsigned XML signature
<xforms:model>
  <xforms:instance xmlns="" id="Generated">
    <data>
      <page1>
        <customer>
          <firstname1>John</firstname1>
          <middlename1>M</middlename1>
          <lastname1>Smith</lastname1>
          <ssn1>123456789</ssn1>
          <streetaddress1>1000 Main Street</streetaddress1>
          <city1>La</city1>
          <state>California</state>
          <zipcode>10080</zipcode>
          <gender1>M</gender1>
          <dateofbirth1>1980-01-02</dateofbirth1>
          <workphone1>1001234567</workphone1>
          <homephone1>1001236789</homephone1>
        </customer>
        <signature1>
          <ds:Signature>
            <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/
            REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
            <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#
            rsa-sha1"></ds:SignatureMethod>
            <ds:Reference>
              <ds:Transforms>
              <ds:Transform Algorithm="http://www.w3.org/2002/06/
                xmldsig-filter2">
                  <dsxp:XPath Filter="subtract">/xfdl:XFDL/xfdl:globalpage/
                  xfdl:global/xfdl:xformsmodels/xforms:model[1]/
                  xforms:instance[@id="Generated"]/data/page1/signature1/
                  ds:Signature
                  </dsxp:XPath>
                  </ds:Transform>
                 </ds:Transforms>
                 <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#
                    sha1"></ds:DigestMethod>
                    <ds:DigestValue></ds:DigestValue>
                  </ds:Reference>
                  <ds:Reference URI="">
                    <ds:Transforms>
                      <ds:Transform Algorithm="http://www.w3.org/2002/06/
                        xmldsig-filter2">
                          <dsxp:XPath Filter="intersect">here()/ancestor::
                          ds:Signature[1]/ds:Object[sigmeta:metadata]</dsxp:XPath>
                        </ds:Transform>
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#
                sha1"></ds:DigestMethod>
                <ds:DigestValue></ds:DigestValue>
              </ds:Reference>
              </ds:SignedInfo>
              <ds:SignatureValue></ds:SignatureValue>
              <ds:Object>
                <sigmeta:metadata>
                  <sigmeta:timestamp>
                    <sigmeta:signtime></sigmeta:signtime>
                    <sigmeta:dst></sigmeta:dst>
                    <sigmeta:date></sigmeta:date>
                  </sigmeta:timestamp>
                </sigmeta:metadata>
              </ds:Object>
            </ds:Signature>
          </signature1>
       </page1>
    </data>
  </xforms:instance>
</xforms:model>

The XML signature includes a Reference element that indicates what to sign. The Reference has no URI attribute, which in an XFDL document means that the whole document should be covered by the signature. The Reference then contains a Transform that subtracts the XML signature being generated. This step is necessary because the XML signature is enveloped within the XFDL document being signed. When the digital fingerprint of the referenced resource (the XFDL document) is calculated, the DigestValue element of the Reference is empty. The result of the calculation is then stored in the DigestValue, which changes the XFDL document. It does not, however, change (XFDL document minus XML signature), which is why we subtract an enveloped signature from the material over which the digest is calculated.

In listing 1, there is a second Reference in the SignedInfo. An XML signature can sign as many resources as the author requires. In this case, we use it to sign additional metadata about the XML signature being generated. This feature can be used, for example, to include a simple XAdES timestamp in the XML signature being generated.

Figure 1. What the sample signed form looks like
Sample signed form

Verification of a signed form

A form can be signed with a private key by a viewing application, such as IBM Lotus Forms Viewer, or by a program. A sample form with an unsigned signature is provided in the Download section. Users are encouraged to download Lotus Forms Viewer to sign the form and to understand how it works. IBM Lotus Forms Server also enables users to digitally sign forms without needing to download and install Lotus Forms Viewer.

Besides Lotus Forms API, a signed form can also be cross-verified using standard Java XML Digital Signature API defined in JSR 105, which is also included as part of Java 6.

To assert that a form has not been tampered with, all signatures in the form must be verified. This verification can be done by finding all XForms instances that contains Signature elements with non-empty SignatureValue elements. For each of such XForms instances, a separate instance document with proper namespaces is created for verification. For performance consideration, a streaming API for XML can be used to pull an XForms instance and parse it into a new Document Object Model (DOM) document.

After the DOM instance document is ready, a DOMValidateContext can be created from a Signature DOM node to be verified. A KeySelector is also needed to extract the public key from the signing certificate in the KeyInfo section. DOMValidateContext uses a custom URI dereferencer in the previous section to perform XML canonization. An XMLSignature object can be created by unmarshaling the DOMValidateContext, and its validate() method can return whether this signature is valid.

After verifying all signatures in all XForms instances, a final result can be gathered to tell the verification status of the entire form.


Handling of the XForms data model

As introduced in the previous section, an XML signature in Lotus Forms is an enveloped signature with markup in an XForms instance. To correctly verify such a signature, the DOM for the XForms instance data must be constructed. W3C XForms specification (http://www.w3.org/TR/xforms/#structure-model-instance) requires that instance data must be extracted into a separated DOM document, and if the instance data was inline within the document then proper namespaces must be inherited from the XForms instance element and its ancestor nodes. All manipulation of data should be performed on the separated DOM document.

This approach means that a document has to be created by cloning a DOM subtree from the content of the xform:instance that contains the XML signature. The new document should contain comment and PI nodes and the only element child from xforms:instance. For the signed form corresponding to the one given in listing 1, a new document with <data> as the document element must be created with namespaces inherited from all ancestor nodes. This document is called an instance document, whereas a source document is called a form document.

Listing 2. Namespaces of extracted document element
<data xmlns="" xmlns:c14n="http://www.w3.org/2001/10/xml-exc-c14n#"
 xmlns:custom="http://www.ibm.com/xmlns/prod/XFDL/Custom" 
 xmlns:designer="http://www.ibm.com/xmlns/prod/workplace/forms/designer/2.6" 
 xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 
 xmlns:dsxp="http://www.w3.org/2002/06/xmldsig-filter2" 
 xmlns:ev="http://www.w3.org/2001/xml-events" 
 xmlns:sigmeta="http://www.ibm.com/xmlns/prod/forms/signature/metadata/1.0" 
 xmlns:xfdl="http://www.ibm.com/xmlns/prod/XFDL/7.6" 
 xmlns:xforms="http://www.w3.org/2002/xforms" 
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
…
</data>

Because a namespace prefix in a parent node can be overridden by a child node, for example default namespace, a namespace is not copied again if it has been copied to the target. This approach respects the namespace overridden rule so that only the closest namespace definition takes effect.

Listing 3. Code to copy namespaces from all ancestors of a source node to a target node
private void cloneNamespaces(Element src, Element target) {
    HashMap copiedNamespaces = new HashMap();

    Node node = src;
    while (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
        NamedNodeMap attributes = node.getAttributes();
        for (int i = 0; i < attributes.getLength(); i++) {
            Attr attr = (Attr) attributes.item(i);
            String namespaceUri = attr.getNamespaceURI();
            if (!XML_NS_URI.equals(namespaceUri)) {
                // a normal attribute, don't copy it
                continue;
            }

            String nodeValue = attr.getNodeValue();
            String localName = attr.getLocalName();
            String prefix = attr.getPrefix();
            String qualifiedName = (prefix == null) ? localName : prefix
                    + ":" + localName; //$NON-NLS-1$

            // don't overwrite namespace already copied.
            if (copiedNamespaces.containsKey(qualifiedName)) {
                continue;
            }

            Document factory = target.getOwnerDocument();
            Attr newAttr = factory.createAttributeNS(namespaceUri,
                    qualifiedName);
            newAttr.setNodeValue(nodeValue);
            target.setAttributeNodeNS(newAttr);

            copiedNamespaces.put(qualifiedName, nodeValue);
        }

        node = node.getParentNode();
    }
}

Custom URI dereferencer

Because the XForms processing model requires that the instance data containing the signature be instantiated separately from the source form document (the XFDL document in this case), a reference with URI="" (same document reference) means a reference to the start of the instance document, not the whole XFDL document. We want to be able to sign the whole XFDL document, not just the data, to create a secure agreement or contract between signing parties.

Standard XML signatures allow a special Reference: a Reference without URI attribute. It is defined by the XML signature specification that the receiving application should be able to identify which object to use in this case. In Lotus Forms, the URI-less Reference refers to the entire XFDL document. Therefore, a custom dereferencer is required to know how to get all referenced data from URIs in all type of references, including a reference without a URI attribute. This customized dereferencer implements javax.xml.crypto.URIDereferencer and is called NoUriDereferencer.

NoUriDereferencer is constructed with the delegation pattern. During dereferencing, if the URI attribute for the URIReference object is found to be null, the form document is returned as an octet stream (although it would be more efficient to return the XFDL document node set, an octet stream is required by javax.xml.crypto.URIDereference> document for reference other than same document reference). For all other URIs, the class delegates to the original default dereferencer provided by the implementation of standard JSR 105.

Listing 4. Dereferencer code
public static class NoUriDereferencer implements URIDereferencer
{
    private InputStream inputStream;

    public NoUriDereferencer(InputStream inputStream) throws IOException
    {
        this.inputStream = inputStream;
    }

    public Data dereference(URIReference uriReference, XMLCryptoContext context)
        throws URIReferenceException
    {
        if (uriReference.getURI() == null)
        {
            Data data = new OctetStreamData(this.inputStream);
            return data;
        }
        else
        {
            URIDereferencer defaultDereferencer = XMLSignatureFactory.getInstance("DOM").
                getURIDereferencer();
            return defaultDereferencer.dereference(uriReference, context);
        }
    }
}

Validate certificates

For a signed signature in Lotus Forms, the certificate chain is put into KeyInfo of Signature element with the first certificate being the signing certificate. The certificate chain can be verified by the Java Certification Path API. Furthermore, if a list of trusted subject names is maintained, the subject of signing certificate can be compared to see if it matches any subject in the trusted list. This step can ensure that the signers of form are trusted.


Running the sample code step by step

Required libraries

An implementation of JSR 105 is required for Java 1.4 or 5. Apache XML Security Java library is used in this article. Because the Java XML Digital Signature API is already included as part of JDK 6, no extra JAR file is required for Java 6.

Instructions for Java 6

  1. Download the ZIP file that contains all sample code and forms from the Download section of this article.
  2. Extract the file into a directory, such as /forms.
  3. Change to directory /forms/xmldsig/bin: cd /forms/xmldsig/bin
  4. Make sure that the correct Java version is used: java -version
  5. Run the sample code and form: java xmldsig.FormVerification ../src/test/resource/form-signed.xfdl

There is one known issue: At the timing of writing, XMLDSig implementation in IBM JDK6 has a namespace canonization bug and does not support the here() function, so the sample form can not be verified with it.

Instructions for Java 1.4 or 5

  1. Download the ZIP file that contains all the sample code and forms from the Download section of this article.
  2. Extract the file into a directory, such as /forms.
  3. Download Apache XML Security library xml-security-bin-1_4_2.zip from http://xml.apache.org/security/dist/java-library/
  4. Extract it to /forms (you can see a directory xml-security-1_4_2 in forms afterward)
  5. Copy xalan.jar, xml-apis.jar, serializer.jar from /forms/xml-security-1_4_2/libs to jre/lib/endorsed if you are using Java version 1.4 from Sun (See http://santuario.apache.org/Java/installation.html)
  6. Change to directory /forms/xmldsig/bin: cd /forms/xmldsig/bin
  7. Make sure that the correct Java version is used: java -version
  8. Run the sample code and form: java -classpath .;../../xml-security-1_4_2/libs/xmlsec-1.4.2.jar;../../xml-security-1_4_2/libs/commons-logging.jar;../../xml-security-1_4_2/libs/xalan.jar xmldsig.FormVerification ../src/test/resource/form-signed.xfdl

Conclusion

Lotus Forms uses the W3C standard for XForms and XML Signature to represent its digital signatures. As a result, a form can be easily verified based on the open standard so that integration into enterprise business process is made possible.


Download

DescriptionNameSize
Sample forms and Java verification codeEPR.zip53KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into IBM collaboration and social software on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Lotus
ArticleID=407394
ArticleTitle=Verifying Lotus Forms XML digital signatures with Java
publish-date=07142009