如何在 C# 中使用 XmlDsigC14NTransform 类

发布于 2024-09-12 03:53:04 字数 3974 浏览 2 评论 0 原文

我正在尝试使用 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

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

I am trying to canonicalize an xml node by using System.Security.Cryptography.Xml.XMLDsigC14nTransform class of c# .net Framework 2.0.

The instance expects three different input types, NodeList, Stream and XMLDocument. I try the transform with all of these input types but I get different results.
What I really want to do is to canonicalize a single node, but as you can see in the output file, the output does not contain any of the inner xml.

Any suggestions about the proper way to canonicalize an XML Node are very appreciated.
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">
  <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>

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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

八巷 2024-09-19 03:53:04

我想,你的答案就在你的问题中,“我真正想做的是规范化单个节点,但正如你在输出文件中看到的那样,输出不包含任何内部 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">
    <child11 Attribute11="c11">Element11</child11>
  </Child1>

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

顺便说一句,沿着以下方向更精确:

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

可能有用,因为 xpath 评估可以在到达第一个 时停止,如果您的真实情况,性能增益可能会非常显着数据量大。

I think, your answer is in your question, "What I really want to do is to canonicalize a single node, but as you can see in the output file, the output does not contain any of the inner xml."

If I understand you, then really you don't want to canonicalise a single node, or you'd be happy that it doesn't contain the inner XML. You want to canonicalise a single subtree.

XPath returns nodes, not subtrees. Some operations on the nodes returned by an XPath expression will then include their child and attribute nodes by default, but canonicalisation deliberately isn't one of these, as potentially some of those very child nodes could be mutable in ways that you are not signing. In signing, you are only signing precisely those nodes that you say you are signing.

Changing the line in your code from:

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

to:

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

Means I get the following in child1.xml:

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

Am I correct in thinking that this is what you want?

Incidentally, more precision along the lines of:

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

May be useful, as then the xpath evaluation can stop when it gets to the first </Child1>, with a performance gain that could be significant if your real data is large.

与之呼应 2024-09-19 03:53:04

我可能在 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">
  <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>

希望它有帮助。
托比亚斯

I found probably the solution at MSDN If I got 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">
  <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>

Hope it helped.
Tobias

尐籹人 2024-09-19 03:53:04

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

您可以尝试不同的 XPath,例如 //child1/* 或 //child1|//child1/* 或 //child1// 或显式的 Nodes() 选择(在 http://msdn.microsoft.com/en-us/library/ms256471.aspx),但您处于灰色地带-用错误赌博。

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

同时启动 Reflector 并看一下 - 这个类可能不是很复杂。

Have you checked MSDN: http://msdn.microsoft.com/en-us/library/fzh48tx1.aspx Sample on their page has a comment which says that "This transform does not contain inner XML elements" - meaning a known issue.

You could try different XPaths like //child1/* or //child1|//child1/* or //child1// or explicit nodes() selection (check full XPath syntax at http://msdn.microsoft.com/en-us/library/ms256471.aspx) but you are in a gray zone - gambling with a bug.

So, in your ms2.xml is the actual output you wanted you'll just have to do that intermediary serialization for the time being.

Also fire up Reflector and take a look - the class is probably not terribly complicated.

昔日梦未散 2024-09-19 03:53:04

关于如何处理 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));
}

然后在处理之前规范换行符,但保留显式内容。

A separate answer on how to deal with the fact that XmlDocument doesn't distinguish U+000D in the source (should not be preserved) from explicit references such as in the source (should be preserved).

Instead of:

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

We first create a newline-cleaning 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;
    }
  }
}

We then use this in Loading the xDoc:

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

This then normalises newlines prior to processing, but leaves explicit alone.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文