我正在使用由第三方提供的给定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();
然后我创建了以下元素(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");
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>
Update - Full XML signature process
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);
LAU
标记并为其分配了命名空间。由于LAU
是在计算签名之前文档中的元素,因此它在计算签名时被包含在内。然后,在DOMSignContext
中,我将LAU
标记本身分配为父节点,因此在计算后,签名将作为子元素添加到其中。 - KAD