Как использовать класс XmlDsigC14NTransform в C # - PullRequest
5 голосов
/ 29 июля 2010

Я пытаюсь канонизировать узел xml с помощью System.Security.Cryptography.Xml.XMLDsigC14nTransform класса c # .net Framework 2.0.

Экземпляр ожидает три различных типа ввода, NodeList, Stream и XMLDocument.Я пробую преобразование со всеми этими типами ввода, но я получаю разные результаты.Что я действительно хочу сделать, так это канонизировать один узел, но, как вы можете видеть в выходном файле, вывод не содержит никакого внутреннего xml.

Любые предложения о правильном способе канонизации XML-узла очень приветствуются.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 ]

4 голосов
/ 06 августа 2010

Я думаю, ваш ответ на ваш вопрос: «Что я действительно хочу сделать, так это канонизировать один узел, но, как вы можете видеть в выходном файле, вывод не содержит никакого внутреннего 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]//@*");

Может быть полезно, так как тогда оценка xpath может прекратиться, когда дойдет до первого </Child1>, с выигрышем в производительности, который может быть значительным, если ваши реальные данные велики.

1 голос
/ 02 августа 2010

Я нашел, вероятно, решение в 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>

Надеюсь, это помогло. Tobias

0 голосов
/ 10 августа 2010

Отдельный ответ о том, как бороться с тем фактом, что 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));
}

Это затем нормализует символы новой строки перед обработкой, но оставляет явное & # xD; один.

0 голосов
/ 03 августа 2010

Вы проверили 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), но вы находитесь в серой зоне - азартные игрыс ошибкой.

Итак, в вашем ms2.xml есть фактический вывод, который вы хотели, вам просто нужно сделать эту промежуточную сериализацию на данный момент.

Также запустите Reflector и выполнитевзгляд - класс, вероятно, не очень сложный.

...