Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8037380

xml canonicalization (c14n) does not work according to specification

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not an Issue
    • Icon: P3 P3
    • None
    • 7u51
    • security-libs

      FULL PRODUCT VERSION :
      java version "1.7.0_51"
      Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
      Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 6.1.7600]

      A DESCRIPTION OF THE PROBLEM :
      The problem is related to XML crypto. The XML canonicalization does not work according to spec:
      http://www.w3.org/TR/xml-c14n

      I have discovered the problem when trying to sign a detached nodeset (as would be the case when creating a signature with a reference to a detached xml, and adding canonicalization tranform to the reference).

      I have found similar problems in IBM JRE and they are already working of a fix (PMR 22754,001,866).

      Trying to canonicalize the following example (based on example from specs) creates wrong result:

      <doc>
         <e1 />
         <e2 ></e2>
         <e3 name = "elem3" id="elem3" />
         <e4 name="elem4" id="elem4" ></e4>
         <e5 a:attr="out" b:attr="sorted" attr2="all" attr="I'm"
            xmlns:b="http://www.ietf.org"
            xmlns:a="http://www.w3.org"
            xmlns="http://example.org"/>
         <e6 xmlns="" xmlns:a="http://www.w3.org">
            <e7 xmlns="http://www.ietf.org">
               <e8 xmlns="" xmlns:a="http://www.w3.org">
                  <e9 xmlns="" xmlns:a="http://www.ietf.org"/>
               </e8>
            </e7>
         </e6>
      </doc>


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      See attached demo code.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The spec is here:
      http://www.w3.org/TR/xml-c14n#Example-SETags

      The expected result should be:

      <doc>
         <e1></e1>
         <e2></e2>
         <e3 id="elem3" name="elem3"></e3>
         <e4 id="elem4" name="elem4"></e4>
         <e5 xmlns="http://example.org" xmlns:a="http://www.w3.org" xmlns:b="http://www.ietf.org" attr="I'm" attr2="all" b:attr="sorted" a:attr="out"></e5>
         <e6 xmlns:a="http://www.w3.org">
            <e7 xmlns="http://www.ietf.org">
               <e8 xmlns="">
                  <e9 xmlns:a="http://www.ietf.org"></e9>
               </e8>
            </e7>
         </e6>
      </doc>
      ACTUAL -
      Oracle JRE produces:

      <doc>
         <e1></e1>
         <e2></e2>
         <e3 id="elem3" name="elem3"></e3>
         <e4 id="elem4" name="elem4"></e4>
         <e5 a:attr="out" attr="I'm" attr2="all" b:attr="sorted" xmlns="http://example.org" xmlns:a="http://www.w3.org" xmlns:b="http://www.ietf.org"></e5>
         <e6 xmlns="" xmlns:a="http://www.w3.org">
            <e7 xmlns="http://www.ietf.org">
               <e8 xmlns="" xmlns:a="http://www.w3.org">
                  <e9 xmlns="" xmlns:a="http://www.ietf.org"></e9>
               </e8>
            </e7>
         </e6>
      </doc>

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.io.ByteArrayInputStream;
      import java.io.ByteArrayOutputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.StringWriter;
      import java.util.Iterator;

      import javax.xml.crypto.Data;
      import javax.xml.crypto.NodeSetData;
      import javax.xml.crypto.OctetStreamData;
      import javax.xml.crypto.dsig.CanonicalizationMethod;
      import javax.xml.crypto.dsig.XMLSignatureFactory;
      import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
      import javax.xml.parsers.DocumentBuilder;
      import javax.xml.parsers.DocumentBuilderFactory;
      import javax.xml.transform.OutputKeys;
      import javax.xml.transform.Transformer;
      import javax.xml.transform.TransformerFactory;
      import javax.xml.transform.dom.DOMSource;
      import javax.xml.transform.stream.StreamResult;

      import org.w3c.dom.Document;
      import org.w3c.dom.Element;
      import org.w3c.dom.Node;
      import org.w3c.dom.traversal.DocumentTraversal;
      import org.w3c.dom.traversal.NodeFilter;
      import org.w3c.dom.traversal.NodeIterator;

      public class RunMe {

      public static class NodeSetDataImpl implements NodeSetData, Iterator<Node> {

      private Node ivNode;

      private NodeFilter ivNodeFilter;

      private Document ivDocument;

      private DocumentTraversal ivDocumentTraversal;

      /** Used by Iterator interface methods. */
      private NodeIterator ivNodeIterator;

      /** Used by Iterator interface methods. */
      private Node ivNextNode;

      public NodeSetDataImpl(Node pNode, NodeFilter pNodeFilter) throws Exception {

      ivNode = pNode;
      ivNodeFilter = pNodeFilter;

      // get Document implementation
      if (ivNode instanceof Document) {
      ivDocument = (Document) ivNode;
      } else {
      ivDocument = ivNode.getOwnerDocument();
      }

      // in DOM implementations supporting DocumentTraversal, the Document interface implementation will also implement the DocumentTraversal interface.
      ivDocumentTraversal = (DocumentTraversal) ivDocument;
      }

      private NodeSetDataImpl(NodeIterator pNodeIterator) {
      ivNodeIterator = pNodeIterator;
      }

      @Override
      public Iterator<Node> iterator() {
      // create a node iterator that will traverse the node's subtree (including the node)
      NodeIterator nodeIterator = ivDocumentTraversal.createNodeIterator(ivNode, NodeFilter.SHOW_ALL, ivNodeFilter, false);
      return new NodeSetDataImpl(nodeIterator);
      }

      private Node checkNextNode() {
      if (ivNextNode == null && ivNodeIterator != null) {
      ivNextNode = ivNodeIterator.nextNode();
      if (ivNextNode == null) {
      // release nodeIterator when not required any more
      ivNodeIterator.detach();
      ivNodeIterator = null;
      }
      }
      return ivNextNode;
      }

      private Node consumeNextNode() {
      Node nextNode = checkNextNode();
      // allow to get next node from the ivNodeIterator
      ivNextNode = null;
      return nextNode;
      }

      @Override
      public boolean hasNext() {
      return checkNextNode() != null;
      }

      @Override
      public Node next() {
      return consumeNextNode();
      }

      @Override
      public void remove() {
      throw new UnsupportedOperationException("Removing nodes is not supported.");
      }

      public NodeFilter getRootNodeFilter() {
      // create a node filter that will skip the fake root when iterating through the DOM
      return new NodeFilter() {
      @Override
      public short acceptNode(Node pNode) {
      if (pNode instanceof Element && pNode.getParentNode() instanceof Document) {
      return NodeFilter.FILTER_SKIP;
      }
      return NodeFilter.FILTER_ACCEPT;
      }
      };
      }

      }

      private static final String C14N_EXAMPLE =
      "<doc>\n" +
      " <e1 />\n" +
      " <e2 ></e2>\n" +
      " <e3 name = \"elem3\" id=\"elem3\" />\n" +
      " <e4 name=\"elem4\" id=\"elem4\" ></e4>\n" +
      " <e5 a:attr=\"out\" b:attr=\"sorted\" attr2=\"all\" attr=\"I'm\"\n" +
      " xmlns:b=\"http://www.ietf.org\"\n" +
      " xmlns:a=\"http://www.w3.org\"\n" +
      " xmlns=\"http://example.org\"/>\n" +
      " <e6 xmlns=\"\" xmlns:a=\"http://www.w3.org\">\n" +
      " <e7 xmlns=\"http://www.ietf.org\">\n" +
      " <e8 xmlns=\"\" xmlns:a=\"http://www.w3.org\">\n" +
      " <e9 xmlns=\"\" xmlns:a=\"http://www.ietf.org\"/>\n" +
      " </e8>\n" +
      " </e7>\n" +
      " </e6>\n" +
      "</doc>";

      public static void testCanonicalization() {

      try {
      String inXml = C14N_EXAMPLE;
      Document inDocument = parse(inXml);
      Data inData = new NodeSetDataImpl(inDocument, null);

      // Canonicalize node set
      byte[] nodeSetCanonicalized = canonizalizeC14N(inData);
      // Verify result of canonicalization
      String message = "Canonicalization produced got:\n" + new String(nodeSetCanonicalized);

      log(message);

      } catch (Exception e) {
      log("ERROR: Testing canonicalization failed.", e);
      }

      }

      public static void log(String message) {
      log(message, null);
      }

      public static void log(String message, Exception exception) {
      System.out.println(message);
      if (exception != null) {
      exception.printStackTrace(System.out);
      }
      System.out.println();
      }

      public static void main(String[] args) {

      testCanonicalization();

      }

      public static byte[] canonizalizeC14N(Data data) throws Exception {
      XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
      CanonicalizationMethod canonicalizationMethod = fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null);
      OctetStreamData output = (OctetStreamData) canonicalizationMethod.transform(data, null);
      return readStream(output.getOctetStream());
      }

      public static Document parse(String pXMLString) throws Exception {
      byte[] xmlBytes = pXMLString.getBytes("UTF-8");
      ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xmlBytes);
      DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
      DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
      return documentBuilder.parse(byteArrayInputStream);
      }

      public static byte[] readStream(InputStream pInputStream) throws IOException {
      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
      byte[] temp = new byte[10000];
      int read;
      while ((read = pInputStream.read(temp)) > 0) {
      byteArrayOutputStream.write(temp, 0, read);
      }
      return byteArrayOutputStream.toByteArray();
      }
      }

      ---------- END SOURCE ----------

            mullan Sean Mullan
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: