如何在C#中使用XmlDsigC14NTransform类

我试图通过使用c#.net Framework 2.0的System.Security.Cryptography.Xml.XMLDsigC14nTransform类来规范化xml节点。

该实例需要三种不同的输入类型:NodeList,Stream和XMLDocument。 我尝试使用所有这些输入类型的转换,但我得到不同的结果。 我真正想要做的是规范化单个节点,但正如您在输出文件中看到的那样,输出不包含任何内部xml。

任何有关规范化XML节点的正确方法的建议都非常受欢迎。 最好,

 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

    Element11   Element21 Element22    Element311    

Child1.xml

  

doc.xml

 
 
 Element11
 
 
 Element21
 Element22
 
 
 
 Element311
 
  
  

ms.xml

   Element11   Element21 Element22    Element311    

ms2.xml

  Element11  

我想,你的答案在你的问题中,“我真正想做的是规范化单个节点,但正如你在输出文件中看到的那样,输出不包含任何内部xml。”

如果我理解你,那么你真的不想规范单个节点,或者你会很高兴它不包含内部XML。 您希望规范化单个子树

XPath返回节点,而不是子树。 默认情况下,XPath表达式返回的节点上的某些操作将包括它们的子节点和属性节点,但故意规范化并不是其中之一,因为这些子节点中的一些可能会以您未签名的方式变为可变。 在签名时,您只是准确地签署了您说要签名的节点。

更改代码中的行:

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

至:

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

意味着我在child1.xml中获得以下内容:

 
 Element11
  

我认为这是你想要的吗?

顺便提一下,更精确的是:

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

可能很有用,因为xpath评估可以在到达第一个时停止,如果您的实际数据很大,性能增益可能很大。

我发现可能在MSDN上的解决方案如果我正确地解决了问题。

这会解决问题吗?:

 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()); 

在child1.xml我有:

 
 
 Element11
 
 
 Element21
 Element22
 
 
 
 Element311
 
 
  

希望它有所帮助。 托比亚斯

你检查过MSDN: http : //msdn.microsoft.com/en-us/library/fzh48tx1.aspx他们页面上的示例有一条评论,上面写着“此转换不包含内部XML元素” – 这意味着一个已知问题。

您可以尝试不同的XPath,如// child1 / *或// child1 | // child1 / *或// child1 //或显式节点()选择(检查完整的XPath语法,请访问http://msdn.microsoft.com/en -us / library / ms256471.aspx )但是你处于一个灰色区域 – 赌博带有一个bug。

所以,在你的ms2.xml中你想要的实际输出你只需暂时进行中间序列化。

同时启动Reflector并查看 – 该类可能不是非常复杂。

关于如何处理XmlDocument不区分源(不应保留)中的U + 000D与显式引用(例如 )的单独答案 在源(应保留)。

代替:

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

我们首先创建一个换行 – 清理TextReader:

 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; } } } 

然后我们在加载xDoc时使用它:

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

然后,这会在处理之前对换行进行标准化,但仅保留显式。