2010-07-29 24 views
4

我想通過使用System.Security.Cryptography.Xml.XMLDsigC14nTransform類的c#.net Framework 2.0來規範化一個xml節點。如何在C#中使用XmlDsigC14NTransform類

實例需要三種不同的輸入類型: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> 

回答

3

我認爲,你的回答是在你的問題中,「我真正想做的是規範化單個節點,但正如你在輸出文件中看到的,輸出不包含任何內部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]//@*"); 

可能是有用的,因爲當它到達第一</Child1>,具有性能增益,如果你真正的,可能是顯著XPath計算可以停止數據很大。

+0

該輸出似乎是正確的。非常感謝您的寶貴時間。 – artsince 2010-08-08 14:24:31

+0

我比較結果與我寫的一些java代碼。用java我不會在行尾看到「 」。哪一個是正確的實施? – artsince 2010-08-09 09:40:32

+0

如果信息源在達到上述處理時已獲得元素內容中的U + 000D個字符,則 表單是正確的,如果沒有,則另一個表單是正確的。這種差異可能不在規範化,而是在基礎流讀取或文檔加載中。我懷疑其中一個或其他實現是「修復」換行符。無論哪種做法都是有缺陷的,不過就你如何獲取XML和/或節點而言,不是c14n。 – 2010-08-09 12:38:22

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> 

希望它幫助。 Tobias

+0

Helo tobias。我仍然無法獲得規範化節點中的內部xml。我看到你正在使用另一種canonizalization算法。我必須嚴格使用XmlDsigC14NTransdormUrl。無論如何,更改算法或使用LoadInnerXml方法似乎不會影響輸出。感謝您的時間和精力。 – artsince 2010-08-03 07:28:07

+0

我加了我的child1.xml。這不是你想要的嗎?你的預期結果是什麼? – schoetbi 2010-08-03 08:22:28

+0

我想要的就是這樣。。我只想規範化Child1節點,而不是整個文檔。我無法用c#方法獲得它。我使用一些內部的java方法,我可以得到合理的輸出。這種差異實際上令人討厭,並且是另一個問題的主題。再次感謝 – artsince 2010-08-06 18:54:01

0

您是否檢查過MSDN:http://msdn.microsoft.com/en-us/library/fzh48tx1.aspx其頁面上的示例有一條評論,指出「此轉換不包含內部XML元素」 - 表示已知問題。

您可以嘗試不同的XPath,如// child1/*或// child1 | // child1/*或// child1 //或顯式節點()選擇(檢查http://msdn.microsoft.com/en-us/library/ms256471.aspx上的完整XPath語法),但是您處於灰色地帶 - 用蟲子賭博。

因此,在你的ms2.xml中是你想要的實際輸出,你只需要暫時做中間序列化。

同時啓動反射器,並看看 - 類可能不是非常複雜。

+0

我們實際上研究了Reflector的規範化代碼,並對它的工作原理有了一些瞭解。我想我們也應該研究在簽名時如何處理canonicalization,因爲有很多內部節點涉及到signedInfo標記。 – artsince 2010-08-04 07:11:49

+0

這不是一個已知問題,它是由相關的W3C RECs設計並堅持的。 – 2010-08-07 16:15:33

+0

所以這是一個設計缺陷? :-)如果你把它和父節點都放在一起,它會刪除那個孩子然後重新連接它嗎?或者把一棵樹變成一片森林?說// // Child2 [1] | // Child2 [1]/* 請注意,我並不是在質疑這個想法的好意(防止無意簽署1K時足夠10K),而是明顯的自相矛盾基於任意節點列表的刪除節點(我可以構造一個更迂迴 - 只是一個根和一個節點3層深,沒有任何中間:-)。 – ZXX 2010-08-07 23:55:06

0

有關如何處理XmlDocument不能區分源(不應保留)中的U + 000D和源(如應保留)中的明確引用(如&#xd;)的事實的單獨答案。

相反的:

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

我們首先創建一個新行清洗的TextReader:

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

這就正常化換行符:

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使用在處理之前,只留下明確的 。