-
Bug
-
Resolution: Not an Issue
-
P3
-
None
-
7u51
-
x86_64
-
windows_7
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 ----------
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 ----------