5

我正在尝试通过使用System.Security.Cryptography.Xml.XMLDsigC14nTransformc# .net Framework 2.0 类来规范化 xml 节点。

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

标志.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>

文档.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

4 回答 4

4

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

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

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

更改代码中的行:

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

到:

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

意味着我在 child1.xml 中得到以下信息:

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

我认为这就是你想要的是否正确?

顺便说一句,更精确的是:

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

可能很有用,因为当它到达 first 时,xpath 评估可能会停止,</Child1>如果您的真实数据很大,性能提升可能会非常显着。

于 2010-08-06T19:22:52.393 回答
1

如果我正确解决了问题,我可能在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 我有:

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

希望它有所帮助。托拜厄斯

于 2010-08-02T18:42:44.507 回答
0

您是否检查过 MSDN: http: //msdn.microsoft.com/en-us/library/fzh48tx1.aspx他们页面上的示例有一条评论说“此转换不包含内部 XML 元素” - 表示已知问题。

您可以尝试不同的 XPath,例如 //child1/* 或 //child1|//child1/* 或 //child1// 或显式节点()选择(在http://msdn.microsoft.com/en查看完整的 XPath 语法-us/library/ms256471.aspx)但您处于灰色区域 - 赌博有错误。

因此,在您的 ms2.xml 中是您想要的实际输出,您暂时只需要进行中间序列化。

还启动 Reflector 并查看一下——这个类可能不是很复杂。

于 2010-08-03T11:41:09.027 回答
0

关于如何处理 XmlDocument 不区分源中的 U+000D (不应保留)与诸如&#xd;源中的显式引用(应保留)这一事实的单独答案。

代替:

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

然后,这会在处理之前对换行符进行规范化,但不考虑显式。

于 2010-08-10T14:46:20.907 回答