XMLSignature created in Java is different from C #

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&#xD;DEF&#xD;HIJ&#xD;</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&#xD;DEF&#xD;GHI&#xD;</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&#xD;DEF&#xD;GHI&#xD;</content></inbound></Body> 

I really do not understand. Why CR is replaced with &#xD; instead of LF, for example, when a document is built from parsing an input string?

+6
source share
1 answer

You may have problems with your canonization if the document you are publishing is your actual input. In this case, they do not keep leading spaces between the opening and closing tags. Do you do any processing in the document before canonization? In a simple test program:

 public static String test = "<Body>\n" + " <inbound>\n" + " <content>...text\r content\n...</content>\n" + " </inbound>\n" + "</Body>"; public static void main(String[] args) throws Exception { com.sun.org.apache.xml.internal.security.Init.init(); Canonicalizer c14n = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); Element body = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(test.getBytes())).getDocumentElement(); byte outputBytes[] = c14n.canonicalizeSubtree(body); System.out.println(new String(outputBytes)); } 

the output of the space is preserved, as it should be (provided that the line separator is normalized). I believe that you are doing something in the code that precedes your canonicalization in Java, which eludes your carriage returns. Maintaining spaces in XML canonicalizations is surprising to most people, see W3C Canonicalization Features . It definitely baffled me the first time I had to handle it.

Do you use XML digital signatures or custom signatures? If W3C, you should be able to use your own digital signature libraries.

In response to editing 1 & 2

The Document.createTextNode (String data) method speeds up data retrieval for you. This is to ensure that you cannot create invalid XML documents (they will also be deleted>, <, and other characters). Instead of performing the normalization of the line separator, determined by XML canonicalization, it escapes characters so that the source text can be restored. Since you seem to want to normalize the line yourself, you must manually sanitize the input lines:

  String text = "ABC\rDEF\rGHI\r"; payload.addTextNode(text.replace("\r", "\n")); 

Using CDATA partitions may also be your solution.

+2
source

Source: https://habr.com/ru/post/979267/


All Articles