当需要创建数字签名并发送给对等方进行验证时,通常会使用 XML 规范化。由于数字签名是基于 XML 数据创建的,因此 XML 数据必须先进行规范化,然后才能计算其签名值。即使是一个额外的空格也可能会影响计算出的签名值,因此它必须遵循一些规则来规范化 XML 数据,使其具有标准格式。这就是 W3C 创建 规范 XML 版本 1.1 规范的原因。
此规范提供了格式化元素节点、属性节点和命名空间节点等的规则。不同的编程语言已经实现了此规范,因此我们可以使用它们轻松地规范化 XML 数据,而无需了解有关该规范的详细信息。
在本教程中,我们不会深入探讨该规范的细节,我们将只关注如何调用 Java API 来规范化 XML 数据。基于 XPath 数据模型,XML 文档由一组节点(节点集)表示。而 Java 中的 XML API 接收一个要规范化的 Data 对象。为了演示规范化,我们将创建一个自定义的 NodeSetDataImpl,它实现了 NodeSetData 和 Iterator 接口。
import java.util.Iterator;
import javax.xml.crypto.NodeSetData;
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 NodeSetDataImpl implements NodeSetData, Iterator {
private Node ivNode;
private NodeFilter ivNodeFilter;
private Document ivDocument;
private DocumentTraversal ivDocumentTraversal;
private NodeIterator ivNodeIterator;
private Node ivNextNode;
public NodeSetDataImpl(Node pNode, NodeFilter pNodeFilter) throws Exception {
ivNode = pNode;
ivNodeFilter = pNodeFilter;
if (ivNode instanceof Document) {
ivDocument = (Document) ivNode;
} else {
ivDocument = ivNode.getOwnerDocument();
}
ivDocumentTraversal = (DocumentTraversal) ivDocument;
}
private NodeSetDataImpl(NodeIterator pNodeIterator) {
ivNodeIterator = pNodeIterator;
}
public Iterator iterator() {
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) {
ivNodeIterator.detach();
ivNodeIterator = null;
}
}
return ivNextNode;
}
private Node consumeNextNode() {
Node nextNode = checkNextNode();
ivNextNode = null;
return nextNode;
}
public boolean hasNext() {
return checkNextNode() != null;
}
public Node next() {
return consumeNextNode();
}
public void remove() {
throw new UnsupportedOperationException("Removing nodes is not supported.");
}
public static NodeFilter getRootNodeFilter() {
return new NodeFilter() {
public short acceptNode(Node pNode) {
if (pNode instanceof Element && pNode.getParentNode() instanceof Document) {
return NodeFilter.FILTER_SKIP;
}
return NodeFilter.FILTER_ACCEPT;
}
};
}
}
然后我们需要创建一个 CanonicalizationMethod 来执行实际的规范化。这里有几个选择:
CanonicalizationMethod.INCLUSIVE CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS CanonicalizationMethod.EXCLUSIVE CanonicalizationMethod.EXCLUSIVE_WITH_COMMENTS
在本教程中,我们将只演示包含式规范化。以下是执行此操作的代码。
import java.io.ByteArrayInputStream;
import javax.xml.crypto.Data;
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 org.w3c.dom.Document;
public class CanonicalizationTest {
private static String INPUT =
"" +
"hello" +
"world" +
"";
public static void main(String[] args){
transform(INPUT);
}
/**
* Create new document with data
*
* @param xml
* @return
*/
private static Document createNewDocument(String xml){
try{
byte[] bytes = xml.getBytes("UTF-8");
ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
fac.setNamespaceAware(true);
DocumentBuilder docBuilder = fac.newDocumentBuilder();
Document doc = docBuilder.parse(bin);
return doc;
} catch (Exception ex){
ex.printStackTrace();
}
return null;
}
/**
* Transform a XML string
*
* @param xml
*/
private static void transform(String xml){
Document doc = createNewDocument(xml);
try{
Data data = new NodeSetDataImpl(doc, NodeSetDataImpl.getRootNodeFilter());
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
CanonicalizationMethod canonicalizationMethod = fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec)null);
// Doing the actual canonicalization
OctetStreamData transformedData =(OctetStreamData) canonicalizationMethod.transform(data, null);
byte[] bytes = tool.Util.readStream(transformedData.getOctetStream());
String str = new String(bytes);
System.out.println(str);
} catch (Exception ex){
ex.printStackTrace();
}
}
}
首先让我们看一下输出:
<my:Node xmlns:my="http://example.com" Id="1">hello</my:Node><my:Node xmlns:my="http://example.com" Id="2">world</my:Node>
测试将首先创建一个 Document,然后将此 Document 传递给 NodeSetDataImpl 以创建要规范化的节点集。从输出中我们可以看到,Root 节点在规范化数据中被删除,这是因为 NodeSetDataImpl 中的 NodeFilter 过滤了此节点。接下来,第二个 my:Node 在规范化形式中,xmlns:my 节点在 Id 节点之前。这是基于规范 XML 规范,其中节点应按词汇顺序排列。
对于其他规范化方法,这里的逻辑是相同的。不同之处在于输出不相同,因为不同的规范化方法有不同的规则。
No comment for this article.