如何在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)); }
然后,这会在处理之前对换行进行标准化,但仅保留显式。