How to use the XmlDsigC14NTransform class in C #

I am trying to canonicalize the xml node using the System.Security.Cryptography.Xml.XMLDsigC14nTransform class C # .net Framework 2.0.

An instance expects three different input types: NodeList, Stream, and XMLDocument. I try to convert all these input types, but I get different results. I really want to canonize one node, but as you can see in the output file, the output does not contain any internal xml.

Any suggestions on the right way to canonicalize XML node are very welcome. Best,

 string path = @"D:\Test\xml imza\sign.xml"; XmlDocument xDoc = new XmlDocument(); xDoc.PreserveWhitespace = true; using (FileStream fs = new FileStream(path, FileMode.Open)) { xDoc.Load(fs); } // canon node list XmlNodeList nodeList = xDoc.SelectNodes("//Child1"); XmlDsigC14NTransform transform = new XmlDsigC14NTransform(); transform.LoadInput(nodeList); MemoryStream ms = (MemoryStream)transform.GetOutput(typeof(Stream)); File.WriteAllBytes(@"D:\Test\xml imza\child1.xml", ms.ToArray()); // canon XMLDocument transform = new XmlDsigC14NTransform(); transform.LoadInput(xDoc); ms = (MemoryStream)transform.GetOutput(typeof(Stream)); File.WriteAllBytes(@"D:\Test\xml imza\doc.xml", ms.ToArray()); // Document to Stream ms = new MemoryStream(); XmlWriter xw = XmlWriter.Create(ms); xDoc.WriteTo(xw); xw.Flush(); ms.Position = 0; transform = new XmlDsigC14NTransform(); transform.LoadInput(ms); ms = (MemoryStream)transform.GetOutput(typeof(Stream)); File.WriteAllBytes(@"D:\Test\xml imza\ms.xml", ms.ToArray()); // node to stream ms = new MemoryStream(); xw = XmlWriter.Create(ms); nodeList[0].WriteTo(xw); xw.Flush(); ms.Position = 0; transform = new XmlDsigC14NTransform(); transform.LoadInput(ms); ms = (MemoryStream)transform.GetOutput(typeof(Stream)); File.WriteAllBytes(@"D:\Test\xml imza\ms2.xml", ms.ToArray()); 

sign.xml

 <?xml version="1.0" encoding="utf-8" ?> <Root Attr="root" xmlns:test="http://www.test.com/xades#"> <Child1 Cttribute="c3" Attribute1="c1" Bttribute="c2"> <child11 Attribute11="c11">Element11</child11> </Child1> <Child2 Attribute2="c2"> <child21 Attribute21="c21">Element21</child21> <child22 Attribute22="c22">Element22</child22> </Child2> <Child3 Attribute3="c3"> <child31 Attribute32="c31"> <child311 Attribute311="c311">Element311</child311> </child31> </Child3> </Root> 

Child1.xml

 <Child1 xmlns:test="http://www.test.com/xades#"></Child1> 

doc.xml

 <Root xmlns:test="http://www.test.com/xades#" Attr="root">&#xD; <Child1 Attribute1="c1" Bttribute="c2" Cttribute="c3">&#xD; <child11 Attribute11="c11">Element11</child11>&#xD; </Child1>&#xD; <Child2 Attribute2="c2">&#xD; <child21 Attribute21="c21">Element21</child21>&#xD; <child22 Attribute22="c22">Element22</child22>&#xD; </Child2>&#xD; <Child3 Attribute3="c3">&#xD; <child31 Attribute32="c31">&#xD; <child311 Attribute311="c311">Element311</child311>&#xD; </child31>&#xD; </Child3> &#xD; </Root> 

ms.xml

 <Root xmlns:test="http://www.test.com/xades#" Attr="root"> <Child1 Attribute1="c1" Bttribute="c2" Cttribute="c3"> <child11 Attribute11="c11">Element11</child11> </Child1> <Child2 Attribute2="c2"> <child21 Attribute21="c21">Element21</child21> <child22 Attribute22="c22">Element22</child22> </Child2> <Child3 Attribute3="c3"> <child31 Attribute32="c31"> <child311 Attribute311="c311">Element311</child311> </child31> </Child3> </Root> 

ms2.xml

 <Child1 Attribute1="c1" Bttribute="c2" Cttribute="c3"> <child11 Attribute11="c11">Element11</child11> </Child1> 
+4
source share
4 answers

I think your answer in your question is: "I really want to canonize one node, but as you can see in the output file, the output does not contain any internal xml."

If I understand you, then you really do not want to canonize a single node, or you would be happy that it does not contain internal XML. You want to canonize one subtree .

XPath returns nodes, not subtrees. Some operations on nodes returned by an XPath expression will include their child and attribute nodes by default, but canonicalization is not intentionally one of them, since potentially some of these same child nodes may be mutable in ways that you do not sign. When signing, you sign exactly those nodes that, as you say, sign.

Changing a line in your code:

 XmlNodeList nodeList = xDoc.SelectNodes("//Child1"); 

in

 XmlNodeList nodeList = xDoc.SelectNodes("//Child1/descendant-or-self::node()|//Child1//@*"); 

So I get the following in child1.xml:

 <Child1 xmlns:test="http://www.test.com/xades#" Attribute1="c1" Bttribute="c2" Cttribute="c3">&#xD; <child11 Attribute11="c11">Element11</child11>&#xD; </Child1> 

Do I think that this is what you want?

By the way, more accuracy along the lines:

 XmlNodeList nodeList = xDoc.SelectNodes("//Child1[1]/descendant-or-self::node()|//Child1[1]//@*"); 

It may be useful, because then the xpath evaluation may stop when it falls in the first </Child1> , with an increase in performance, which can be significant if your real data is large.

+3
source

I probably found a solution on MSDN if I understood the problem correctly.

Does this solve the problem ?:

 string path = @"sign.xml"; var xDoc = new XmlDocument(); xDoc.PreserveWhitespace = true; using (var fs = new FileStream(path, FileMode.Open)) { xDoc.Load(fs); } // canon node list XmlNodeList nodeList = xDoc.SelectNodes("//Child1"); var transform = new XmlDsigC14NTransform(true) { Algorithm = SignedXml.XmlDsigExcC14NTransformUrl }; var validInTypes = transform.InputTypes; var inputType = nodeList.GetType(); if (!validInTypes.Any(t => t.IsAssignableFrom(inputType))) { throw new ArgumentException("Invalid Input"); } transform.LoadInput(xDoc); var innerTransform = new XmlDsigC14NTransform(); innerTransform.LoadInnerXml(xDoc.SelectNodes("//.")); var ms = (MemoryStream) transform.GetOutput(typeof (Stream)); ms.Flush(); File.WriteAllBytes(@"child1.xml", ms.ToArray()); 

In child1.xml, I have:

 <Root xmlns:test="http://www.test.com/xades#" Attr="root">&#xD; <Child1 Attribute1="c1" Bttribute="c2" Cttribute="c3">&#xD; <child11 Attribute11="c11">Element11</child11>&#xD; </Child1>&#xD; <Child2 Attribute2="c2">&#xD; <child21 Attribute21="c21">Element21</child21>&#xD; <child22 Attribute22="c22">Element22</child22>&#xD; </Child2>&#xD; <Child3 Attribute3="c3">&#xD; <child31 Attribute32="c31">&#xD; <child311 Attribute311="c311">Element311</child311>&#xD; </child31>&#xD; </Child3>&#xD; </Root> 

Hope this helps. Tobias

+1
source

You checked the MSDN: http://msdn.microsoft.com/en-us/library/fzh48tx1.aspx The example on their page contains a comment saying that "this conversion does not contain internal XML elements" - means a known problem.

You can try different XPaths, such as // child 1 / * or // child1 | // child1 / * or // child1 // or explicit node selection () (check the full XPath syntax at http://msdn.microsoft. com / en-us / library / ms256471.aspx ), but you are in the gray zone - gambling with an error.

So, in your ms2.xml there is the actual result that you wanted, you just need to do this intermediate serialization now.

Also run Reflector and take a look - the class is probably not very complex.

0
source

A separate answer to the question of how an XmlDocument does not distinguish between U + 000D in the source (should not be saved) from explicit links such as &#xd; at source (must be saved).

Instead:

 using (FileStream fs = new FileStream(path, FileMode.Open)) { xDoc.Load(fs); } 

First, create a TextReader to clear the lines:

 private class LineCleaningTextReader : TextReader { private readonly TextReader _src; public LineCleaningTextReader(TextReader src) { _src = src; } public override int Read() { int r = _src.Read(); switch(r) { case 0xD:// \r switch(_src.Peek()) { case 0xA: case 0x85: // \n or NEL char _src.Read(); break; } return 0xA; case 0x85://NEL return 0xA; default: return r; } } } 

Then we use this when loading xDoc:

 using (FileStream fs = new FileStream(path, FileMode.Open)) { using(TextReader tr = new StreamReader(fs)) xDoc.Load(new LineCleaningTextReader(tr)); } 

This then normalizes newlines before processing, but leaves explicit & # xD; by oneself.

0
source

All Articles