0

I am failing to understand how to place the signature node as a sibling to the data node. The example that I have found was from Oracle, but only covered the normal detached signature, where the XML is passed as an URI to the Reference object.

Reference to the Oracle example: https://docs.oracle.com/javase/8/docs/technotes/guides/security/xmldsig/GenDetached.java

hllshm
  • 1
  • 2

1 Answers1

0

One way to do this is as follows:

Step 1 - get the signature XML as a string.

Change the sample GenDetached code in your link as follows, so that the output is a string.

The following changes are made at the bottom of the class:

// output the resulting document
OutputStream os = System.out;
//if (args.length > 0) {
//    os = new FileOutputStream(args[0]);
//} else {
//    os = System.out;
//}

// my changes, to write to a string:
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
StringWriter writer = new StringWriter();
trans.transform(new DOMSource(doc), new StreamResult(writer));
return writer.toString();

Also change the method signature so it returns a string - for example:

public class GenDetached {
    public static String generate() throws Exception {
        // code not shown
    }
}

Step 2 - Add the signature data to your target XML document.

Assume we have the following target doc:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
    <data>all your data in here...</data>
</root>

The following code extracts the <Signature> node from the signature XML and adds it as a child of the <root> node in your document:

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.xml.sax.InputSource;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.StringReader;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;

...

String signatureXml = GenDetached.generate();

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
// My test document to hold the data and the signature:
String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><root><data>foo</data></root>";

Document doc = db.parse(new InputSource(new StringReader(xmlString)));
Node docRootNode = doc.getFirstChild();

Document sig = db.parse(new InputSource(new StringReader(signatureXml)));
NodeList sigNodeList = sig.getElementsByTagName("Signature");
Node sigNode = sigNodeList.item(0);

// First import the signature node into the main document:
Node docImportedNode = doc.importNode(sigNode, true);
// Then add the signature node to the main document's root:
docRootNode.appendChild(docImportedNode);

// Show the output (as a String in this case, for the demo):
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
trans.transform(new DOMSource(doc), new StreamResult(writer));
System.out.println(writer.toString());

The result is this (details omitted):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
    <data>foo</data>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            ...
        </SignedInfo>
        <SignatureValue>...</SignatureValue>
        <KeyInfo>
            <KeyValue>
                <DSAKeyValue>
                    ...
                </DSAKeyValue>
            </KeyValue>
        </KeyInfo>
    </Signature>
</root>

Hope that helps.

Small point about the "internally detached" terminology in the question (courtesy of Wikipedia):

An XML signature used to sign a resource outside its containing XML document is called a detached signature; if it is used to sign some part of its containing document, it is called an enveloped signature.

I assume this example is the latter.

andrewJames
  • 19,570
  • 8
  • 19
  • 51
  • Thank you, this is exactly what I needed. One last question, how is the target document referenced, as I imagine just giving a local file's URI is not the ideal option? – hllshm May 13 '20 at 21:06
  • The [documentation](https://www.w3.org/TR/xmldsig-core/) section 4.4.3.1 says "_We recommend XML Signature applications be able to dereference URIs in the HTTP scheme._" And, yes, using a local file URI would not really be suitable. But look at examples 23 & 24 in that same link. They show how to reference the "rest of the document" without invalidating the signature. And in those cases the URI is therefore an empty string (because it's all in the same doc): ``. I've never actually seen such a document (enveloped) in real life, so apologies if this is not helpful. – andrewJames May 13 '20 at 21:13