签署文档时使用的 xslt 转换与计算签名时如何选择源 XML 中的节点有关。
This question/answer by Dave 涉及使用 xpath2 对 XML 文档的部分进行签名。此答案中的 link to Sean Mullans' post 表明 xpath2 更适合签署文档的某些部分,因为 xpath 表达式的评估是按节点完成的。
因此,基于sun dsig example,您可以使用以下方法替换 Reference 创建:
List<XPathType> xpaths = new ArrayList<XPathType>();
xpaths.add(new XPathType("//r1/user", XPathType.Filter.INTERSECT));
Reference ref = fac.newReference
("", fac.newDigestMethod(DigestMethod.SHA1, null),
Collections.singletonList
(fac.newTransform(Transform.XPATH2,
new XPathFilter2ParameterSpec(xpaths))),
null, null);
这允许 //r1/user 使用签名进行保护,同时可以更改文档的其余部分。
xpath/xpath2 选择的问题是可以为 /some/node/that/does/not/exist 生成签名。您修改测试文档并确保签名按您期望的方式工作是正确的。
您可以在测试程序中通过生成签名然后在验证之前篡改xml节点来测试文档:
NodeList nlt = doc.getElementsByTagName("user");
nlt.item(0).getFirstChild().setTextContent("Something else");
xpath 选择器的更可靠替代方法可能是将 ID 放在您希望签名的 xml 文档元素上,例如:
<r1>
<user id="sign1">asd</user>
<person>ghi</person>
</r1>
然后将此 ID 引用为封装传输的第一个参数中的 URI:
Reference ref = fac.newReference
("#sign1", fac.newDigestMethod(DigestMethod.SHA1, null),
Collections.singletonList
(fac.newTransform(Transform.ENVELOPED,(TransformParameterSpec) null)),
null, null);
对于输出,签名操作将一个新的 Signature 元素添加到您在内存中加载的 DOM。您可以通过像这样转换输出来流式传输输出:
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.setOutputProperty(OutputKeys.INDENT, "yes");
trans.transform(new DOMSource(doc), new StreamResult(System.out));