This problem is driving me crazy. I have to sign SOAPMessage using Java, which is verified by the dotNet endpoint application. The provider provided us with an example dotNet client application as an example, so I was inspired by this sample application and a few links on the Internet, for example:
http://www.java2s.com/Tutorial/Java/0410__Web-Services-SOA/SignSOAPmessage.htm
Java equivalent of C # XML Signing Method
Body structure (which is signed content):
<Body> <inbound> <content>...text content...</content> </inbound> </Body>
Until now, I could create identical signatures (using my code and sample provided by the supplier) only if the text content does not contain line breaks. As soon as my text contains a line separator, the signature value no longer matches.
After several days of searching, I think the problem is related to canonicalization.
In Java, the following code:
... Canonicalizer c14n = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); Element body = getNextSiblingElement(header); byte outputBytes[] = c14n.canonicalizeSubtree(body); FileUtils.writeByteArrayToFile(new File("C:\\tmp\\Canonicalized_java.xml"),outputBytes); ...
It produces:
<s:Body xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" Id="uuid"><inbound><content>ABC
DEF
HIJ
</content></inbound></s:Body>
And in dotNet (C #), the following code:
... using (MemoryStream nodeStream = new MemoryStream()) { XmlWriterSettings ws = new XmlWriterSettings(); ws.NewLineHandling = NewLineHandling.None; using (XmlWriter xw = XmlWriter.Create(nodeStream, ws)) { soapRequest.GetElementsByTagName("Body")[0].WriteTo(xw); xw.Flush(); } nodeStream.Position = 0; XmlDsigExcC14NTransform transform = new XmlDsigExcC14NTransform(); transform.LoadInput(nodeStream); using (MemoryStream outputStream = (MemoryStream)transform.GetOutput(typeof(Stream))) { File.WriteAllBytes("C:\\tmp\\Canonicalized_dotNet.xml", outputStream.ToArray()); } } ...
It produces:
<s:Body xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" Id="uuid"><inbound><content>ABC DEF HIJ </content></inbound></s:Body>
If line breaks are only LF. (Note that the original content contains only CR as a line break).
I can provide more source code if necessary, but maybe someone knows exactly what is going on here. Any help would be greatly appreciated. Note that I cannot change the way dotNet endpoint validates XML signature values.
EDIT This is how the body of the message is built. I do not understand why this does not give the same result as Aaryn anwser:
public static void main(String[] args) throws Exception { com.sun.org.apache.xml.internal.security.Init.init(); SOAPMessage soapMessage = MessageFactory.newInstance().createMessage(); SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope(); SOAPBody soapBody = soapEnvelope.getBody(); SOAPElement inboundMessage = soapBody.addChildElement("inbound"); SOAPElement payload = inboundMessage.addChildElement("content"); payload.addTextNode("ABC\rDEF\rGHI\r"); Canonicalizer c14n = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); byte outputBytes[] = c14n.canonicalizeSubtree(soapBody); System.out.println(new String(outputBytes)); }
output:
<SOAP-ENV:Body xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><inbound><content>ABC
DEF
GHI
</content></inbound></SOAP-ENV:Body>
EDIT 2 I did some more tests, trying to build XML at runtime, instead of parsing the input string. Here is my last test:
public static void main(String[] args) throws Exception { com.sun.org.apache.xml.internal.security.Init.init(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); Element body = doc.createElement("Body"); Element inbound = doc.createElement("inbound"); Element content = doc.createElement("content"); content.appendChild(doc.createTextNode("ABC\rDEF\rGHI\r")); doc.appendChild(body); body.appendChild(inbound); inbound.appendChild(content); Canonicalizer c14n = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); byte outputBytes[] = c14n.canonicalizeSubtree(body); System.out.println(new String(outputBytes)); }
And the way out
<Body><inbound><content>ABC
DEF
GHI
</content></inbound></Body>
I really do not understand. Why CR is replaced with 
instead of LF, for example, when a document is built from parsing an input string?