I am creating the following XML document, using JAXB of a given XSD that is provided by third party. The third party request to sign the document and add to it an extra element that holds the signature. Using JDK 1.7.
Below is the code sample for marshalling:
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();
Then I create the element (LAU) as below and sign the document using HMAC-SHA256
algorithm and the JSR105 JAVA API (I am not going to include the whole signature code to reduce verbosity, I am using using the standard behavior of the XMLSignature
class and then transforming the document to a file output stream from a DOMSource using XML transformers):
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");
The XML is signing properly but when validating, the calculated digest value is different than the digest value.
I have noticed that when changing the namespace value when creating the LAU element, the digest is never changing, as if the document is getting signed and neglecting the namespace of the LAU element, and I guess this is the reason why its failing. Any other change in the document as a whole, or change in the prefix of LAU element directly affects the calculated digest of the payload.
If I append the signature to the root element directly rather than creating the LAU element, the validation works properly.
The LAU element exists within the XSD and can be created using JAXB but the problem is that I cannot find a way to assign a prefix (only for it in the document) of the same namespace as the root element.
Questions:
Is the namespace actually being omitted from the payload digest calculation when adding the element to the document using
createElementNS
andappendChild
?Is there a way to provide through JAXB a prefix for the same root namespace to a single element only?
How can I find the actual XML string being signed by the API, I tried reading the reference input stream after enabling
javax.xml.crypto.dsig.cacheReference
but this did not work when signing, it only worked when validating?
Below is a sample of the 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);