KAD*_*KAD 13 java xml document jaxb xml-signature
我正在使用第三方提供的给定XSD的JAXB创建以下XML文档.第三方请求签署文档并向其添加一个包含签名的额外元素.使用JDK 1.7.
以下是编组的代码示例:
JAXBContext jaxbContext = JAXBContext.newInstance(DataPDU.class);
DataPDU myDataPDU = new DataPDU();
myDataPDU.setRevision("2.0.6");
// marshall the file
Marshaller marshaller = jaxbContext.createMarshaller();
DOMResult domResult = new DOMResult();
marshaller.marshal(myDataPDU, domResult);
// get the document list
Document document = (Document) domResult.getNode();
Run Code Online (Sandbox Code Playgroud)
然后我创建元素(LAU)如下,并使用HMAC-SHA256算法和JSR105 JAVA API 签署文档(我不会包括整个签名代码以减少冗长,我使用XMLSignature类的标准行为,然后转换使用XML转换器将文档记录到DOMSource的文件输出流中:
Element LAUElement = document.createElementNS("urn:swift:saa:xsd:saa.2.0", "Saa:LAU");
Element rootElement = document.getDocumentElement();
rootElement.appendChild(LAUElement);
// sign the document
XMLSignatureUtil.sign(document, secret, LAUElement, "ds");
// create the output file
TransformerUtil.transformDocumentToFile(document, "resultingFile.xml");
Run Code Online (Sandbox Code Playgroud)
XML正确签名但在验证时,计算的摘要值与摘要值不同.
我注意到在创建LAU元素时更改命名空间值时,摘要永远不会改变,就好像文档正在签名并忽略了LAU元素的命名空间,我想这就是它失败的原因.整个文档中的任何其他更改,或LAU元素前缀的更改都会直接影响计算的有效负载摘要.
如果我将签名直接附加到根元素而不是创建LAU元素,则验证可以正常工作.
LAU元素存在于XSD中,可以使用JAXB创建,但问题是我找不到一种方法来为与根元素分配相同名称空间的前缀(仅在文档中).
问题:
当使用
createElementNS和将元素添加到文档时,是否实际上从有效负载摘要计算中省略了命名空间appendChild?有没有办法通过JAXB为同一根命名空间提供前缀到单个元素?
如何找到由API签名的实际XML字符串,我尝试在启用后读取引用输入流,
javax.xml.crypto.dsig.cacheReference但这在签名时不起作用,它只在验证时有效?
以下是XML示例:
<DataPDU xmlns="urn:swift:saa:xsd:saa.2.0">
<Revision>2.0.6</Revision>
<Saa:LAU xmlns:Saa="urn:swift:saa:xsd:saa.2.0"> Signature lies here </Saa:LAU>
</DataPDU>
Run Code Online (Sandbox Code Playgroud)
更新 - 完整的XML签名过程
JAXBContext jaxbContext = JAXBContext.newInstance(DataPDU.class);
DataPDU myDataPDU = new DataPDU();
myDataPDU.setRevision("2.0.6");
// marshall the file
Marshaller marshaller = jaxbContext.createMarshaller();
DOMResult domResult = new DOMResult();
marshaller.marshal(myDataPDU, domResult);
// get the document list
Document document = (Document) domResult.getNode();
// signing process
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
SignatureMethod signatureMethod =
factory.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#hmac-sha256", null);
CanonicalizationMethod canonicalizationMethod =
factory.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (XMLStructure) null);
List<Transform> transforms = new ArrayList<Transform>();
transforms.add(factory.newTransform(Transform.ENVELOPED, (XMLStructure) null));
transforms.add(factory.newTransform("http://www.w3.org/2001/10/xml-exc-c14n#", (XMLStructure) null));
DigestMethod digestMethod = factory.newDigestMethod("http://www.w3.org/2001/04/xmlenc#sha256", null);
Reference reference = factory.newReference("", digestMethod, transforms, null, null);
SignedInfo signedInfo =
factory.newSignedInfo(canonicalizationMethod, signatureMethod, Collections.singletonList(reference));
String secretKey = "Abcd1234abcd1234Abcd1234abcd1234";
SecretKeySpec secret_key = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
Element LAUElement = document.createElementNS("urn:swift:saa:xsd:saa.2.0", "Saa:LAU");
Element rootElement = document.getDocumentElement();
rootElement.appendChild(LAUElement);
DOMSignContext domSignContext = new DOMSignContext(secret_key, LAUElement);
domSignContext.setDefaultNamespacePrefix("ds");
XMLSignature signature = factory.newXMLSignature(signedInfo, null);
signature.sign(domSignContext);Run Code Online (Sandbox Code Playgroud)
如何找到由 API 签名的实际 XML 字符串,我尝试在启用 javax.xml.crypto.dsig.cacheReference 后读取引用输入流,但这在签名时不起作用,仅在验证时起作用?
幸运的是,默认实现(假设正在使用)提供了一些调试功能。这是使用粒度记录到控制台的设置示例FINEST,尽管FINE似乎已经足够了。
handlers= java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = FINER
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
org.jcp.xml.dsig.internal.level = FINER
com.sun.org.apache.xml.internal.security.level = FINER
Run Code Online (Sandbox Code Playgroud)
您可以将其保存为logging.properties文件并通过命令行选项提供其路径-Djava.util.logging.config.file,尽管也可以通过编程方式完成。
这样做实际上会显示正在签名的规范化 XML,这回答了这个问题:
使用 createElementNS 和appendChild 将元素添加到文档时,实际上是否从有效负载摘要计算中省略了命名空间?
它的确是。运行代码,我在日志记录中看到这是用于签名的规范化 XML。
<DataPDU xmlns="urn:swift:saa:xsd:saa.2.0"><Revision>2.0.6</Revision><Saa:LAU></Saa:LAU></DataPDU>
Run Code Online (Sandbox Code Playgroud)
请注意前缀是如何Saa存在的,但名称空间声明却不是。然而,信息就在那里,因为转换后带有前缀的声明显示在结果中。这解释了为什么对命名空间 URI 本身的更改不会导致不同的摘要,但更改前缀却会导致不同的摘要。不知道为什么会发生这种情况。感觉不应该,但是规范化版本中包含的内容的规则在规范中非常混乱,要么我遗漏了一些东西,要么它是实现中的错误。解决此问题的方法是在创建后添加此行LAUElement:
LAUElement.setAttribute("xmlns:Saa", "urn:swift:saa:xsd:saa.2.0");
Run Code Online (Sandbox Code Playgroud)
我认为 DOMResult 可能使用了一个未设置为命名空间感知的文档生成器,但我尝试了这一点,没有任何区别:
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = domFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
DOMResult domResult = new DOMResult(doc);
marshaller.marshal(myDataPDU, domResult);
Run Code Online (Sandbox Code Playgroud)
这可能是 DOMResult 本身的错误。解决方法有点混乱,但它可以完成工作。这实际上确实改变了摘要。我尝试过提供LAUElement和 文档根作为 的输入DOMSignContext,这两种情况都会产生相同的签名(摘要和签名值),尽管在后一种情况下签名被添加到根,而不是元素。
这也告诉我们,即使您提供元素并使用专有的规范化方法,整个文档也将用作签名的输入。它仅更改签名的放置位置。URI 解析找到根元素及其下面的所有元素的祖先。这让我有点惊讶。
执行上述操作后,我成功使用以下代码正确验证了签名:
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
NodeList sigList = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (sigList.getLength() == 0) {
throw new Exception("Cannot find Signature element");
}
String secretKey = "Abcd1234abcd1234Abcd1234abcd1234";
SecretKeySpec secret_key = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
DOMValidateContext valContext = new DOMValidateContext(secret_key, sigList.item(0));
XMLSignature signature = factory.unmarshalXMLSignature(valContext);
System.out.println("Core validity: " + signature.validate(valContext));
Run Code Online (Sandbox Code Playgroud)
确实,问题似乎在于,当删除名称空间声明时,生成的摘要与验证时生成的摘要不同。
有没有办法通过 JAXB 仅为单个元素提供相同根命名空间的前缀?
简短的回答是,没有简单的方法可以做到这一点。长答案可以在这里找到:https ://stackoverflow.com/a/42301479/630136
编辑:
附加说明。请小心使用转换器将签名文档输出到文件。如果将其设置为缩进输出,则生成的文件实际上会有不同的摘要。有些空格可以忽略(如<empty />vs中<empty/>),但在元素内,它通常被视为文本节点和计算摘要的规范化版本的一部分。
| 归档时间: |
|
| 查看次数: |
828 次 |
| 最近记录: |