System.Security.Cryptography.Xml.SignedXml 類別
本文提供此 API 參考文件的補充備註。
類別 SignedXml 是萬維網聯合會 (W3C) XML 簽章語法和處理規格的 .NET 實作,也稱為 XMLDSIG (XML 數位簽名)。 XMLDSIG 是以標準為基礎的互通方式,用來簽署和驗證 XML 檔的所有或部分或其他可從統一資源標識碼 (URI) 尋址的數據。
SignedXml每當您需要以標準方式在應用程式或組織之間共用已簽署的 XML 數據時,請使用 類別。 使用這個類別簽署的任何數據,都可以透過 XMLDSIG 的 W3C 規格實作來驗證。
類別 SignedXml 可讓您建立下列三種 XML 數字簽名:
簽章類型 | 描述 |
---|---|
信封簽章 | 簽章包含在要簽署的 XML 元素內。 |
Enveloping 簽章 | 帶正負號的 <Signature> XML 包含在 項目內。 |
內部中斷連結簽章 | 簽章和已簽署的 XML 位於相同的檔中,但兩個元素都沒有包含另一個。 |
另外還有第四種簽章,稱為外部中斷連結簽章,也就是當數據和簽章位於個別的 XML 檔中時。 類別不支援 SignedXml 外部中斷連結簽章。
XML 簽章的結構
XMLDSIG 會 <Signature>
建立專案,其中包含 XML 檔的數位簽名或其他可從 URI 尋址的數據。 元素 <Signature>
可以選擇性地包含如何尋找密鑰的相關信息,以驗證簽章和用於簽署的密碼編譯演算法。 基本結構如下所示:
<Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>Base64EncodedValue==</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>AnotherBase64EncodedValue===</SignatureValue>
</Signature>
此結構的主要部分包括:
<CanonicalizationMethod>
項目指定將專案從 XML/text 重寫
Signature
為位元組的規則,以進行簽章驗證。 .NET 中的預設值是 http://www.w3.org/TR/2001/REC-xml-c14n-20010315,可識別值得信任的演算法。 這個元素是由 SignedInfo.CanonicalizationMethod 屬性表示。<SignatureMethod>
項目指定用於產生簽章和驗證的
<Signature>
演算法,此演算法會套用至 專案以在 中<SignatureValue>
產生值。 在上述範例中,值 http://www.w3.org/2000/09/xmldsig#rsa-sha1 會識別 RSA PKCS1 SHA-1 簽章。 由於 SHA-1 發生衝突問題,Microsoft 建議根據 SHA-256 或更好的安全性模型。 這個元素是由 SignatureMethod 屬性表示。<SignatureValue>
項目指定 元素的密碼
<Signature>
編譯簽章。 如果此簽章未驗證,則區塊的某些<Signature>
部分遭到竄改,且文件被視為無效。 只要<CanonicalizationMethod>
值值得信任,這個值就高度抵制竄改。 這個元素是由 SignatureValue 屬性表示。項目的
URI
屬性<Reference>
使用 URI 參考指定資料物件。 這個屬性是由 Reference.Uri 屬性表示。
未指定
URI
屬性,也就是將 屬性設定 Reference.Uri 為null
,表示接收應用程式應該知道物件的身分識別。 在大部分情況下,null
URI 會導致擲回例外狀況。 除非您的應用程式與需要 URI 的通訊協定互通,否則請勿使用null
URI。將
URI
屬性設定為空字串,表示檔的根元素正在簽署,這是一種信封簽章形式。如果屬性值以
URI
#开头,則值必須解析為目前檔中的 元素。 此表單可以搭配任何支援的簽章類型使用(信封簽章、封存籤章或內部中斷連結簽章)。任何其他專案都會被視為外部資源中斷連結簽章,且 類別不支援 SignedXml 。
<Transforms>
項目包含已排序的
<Transform>
元素清單,描述簽署者如何取得摘要的數據物件。 轉換演算法類似於標準化方法,但與其重寫<Signature>
元素,而是重寫 元素的屬性<Reference>
所URI
識別的內容。 專案<Transforms>
是由 TransformChain 類別表示。每個轉換演算法都會定義為接受 XML(XPath 節點集)或位元組作為輸入。 如果目前數據的格式與轉換輸入需求不同,則會套用轉換規則。
每個轉換演算法都會定義為產生 XML 或位元組作為輸出。
如果最後一個轉換演算法的輸出不是以位元組定義(或未指定任何轉換),則 標準化方法 會當做隱含轉換使用(即使元素中
<CanonicalizationMethod>
指定了不同的演算法也一樣)。轉換演算法的 值 http://www.w3.org/2000/09/xmldsig#enveloped-signature 會編碼規則,該規則會解譯為從檔中移除
<Signature>
專案。 否則,封包簽章的驗證器會摘要檔,包括簽章,但簽署者會在套用簽章之前摘要檔,導致不同的答案。
<DigestMethod>
項目識別摘要式 (密碼編譯哈希) 方法,以套用至 專案 屬性
<Reference>
所URI
識別的轉換內容。 這是由 Reference.DigestMethod 屬性表示。
選擇標準化方法
除非與需要使用不同值的規格互操作,否則我們建議您使用預設的 .NET 標準化方法,也就是 XML-C14N 1.0 演算法,其值為 http://www.w3.org/TR/2001/REC-xml-c14n-20010315。 XMLDSIG 的所有實作都必須支援 XML-C14N 1.0 演算法,特別是因為它是要套用的隱含最終轉換。
有支援保留批注的正式化演算法版本。 不建議使用批註保留標準方法,因為它們違反了「看過的符號」原則。 也就是說,元素中的 <Signature>
批注不會改變簽章執行方式的處理邏輯,而只會改變簽章值。 結合弱式簽章演算法時,允許包含批注,可讓攻擊者不必要的自由強制哈希衝突,使竄改的檔看起來合法。 在 .NET Framework 中,預設僅支援內建標準工具。 若要支援其他或自定義標準工具,請參閱 SafeCanonicalizationMethods 屬性。 如果檔案使用不在 屬性所 SafeCanonicalizationMethods 表示之集合中的標準化方法,則 CheckSignature 方法會傳回 false
。
注意
極防禦性的應用程式可以移除任何值,而不需要從集合使用 SafeCanonicalizationMethods 簽署者。
參考值是否安全免於竄改?
是, <Reference>
這些值是安全的,不會遭到竄改。 .NET 會在 <SignatureValue>
處理任何 <Reference>
值及其相關聯的轉換之前先驗證計算,並會提早中止,以避免潛在的惡意處理指令。
選擇要簽署的專案
建議您盡可能使用 屬性的 「值(或將 屬性設定Uri為URI
空字串)。 這表示會將整份文件視為摘要計算,這表示整個檔會受到保護,以免遭到竄改。
以錨點的形式查看 URI
值很常見,例如 #foo,指的是標識符屬性為 “foo” 的專案。 不幸的是,這很容易遭到竄改,因為這隻包含目標元素的內容,而不是內容。 濫用此區別稱為 XML 簽章包裝 (XSW)。
如果您的應用程式將批注視為語意(在處理 XML 時並不常見),則您應該使用 “#xpointer(/)” 而不是 “”,而 “#xpointer(id('foo'))”而不是 “#foo”。 #xpointer 版本會解譯為包含批註,而shortname窗體則排除批註。
如果您需要接受僅部分保護的檔,而且您想要確保您讀取受簽章保護的相同內容,請使用 GetIdElement 方法。
KeyInfo 元素的安全性考慮
選擇性 <KeyInfo>
項目中的數據(也就是 KeyInfo 屬性),其中包含用來驗證簽章的索引鍵,不應受信任。
特別是當值代表裸機 RSA、DSA 或 ECDSA 公鑰時 KeyInfo ,檔可能會遭到竄改,儘管 CheckSignature 方法報告簽章有效。 這可能會發生,因為執行竄改的實體只需要產生新的密鑰,並使用該新密鑰重新簽署竄改的檔。 因此,除非您的應用程式確認公鑰是預期的值,否則應該將檔視為遭到竄改。 這需要您的應用程式檢查內嵌在檔案中的公鑰,並針對檔案內容的已知值清單進行驗證。 例如,如果已知使用者可以理解檔是由已知用戶發出,您就會針對該使用者所使用的已知密鑰清單檢查密鑰。
您也可以使用 CheckSignatureReturningKey 方法來處理文件之後驗證索引鍵,而不是使用 CheckSignature 方法。 但是,為了獲得最佳安全性,您應該事先確認密鑰。
或者,請考慮嘗試使用者的已註冊公鑰,而不是讀取 項目中的內容 <KeyInfo>
。
X509Data 元素的安全性考慮
選擇性 <X509Data>
專案是 專案的子系, <KeyInfo>
包含 X509 憑證的一或多個 X509 憑證或標識碼。 元素中的數據 <X509Data>
也不應該原本就受信任。
使用內嵌 <X509Data>
元素驗證檔時,.NET 只會驗證數據解析為 X509 憑證,其公鑰可以成功用來驗證檔簽章。 與使用 參數設為false
的呼叫 CheckSignature 方法verifySignatureOnly
不同,不會執行撤銷檢查、未檢查鏈結信任,也不會驗證任何到期日。 即使您的應用程式擷取憑證本身,並將它 CheckSignature 傳遞給方法 verifySignatureOnly
,並將參數設定為 false
,該參數仍然不足以防止檔竄改。 憑證仍需要驗證為適合簽署的檔。
使用內嵌簽署憑證可以提供實用的密鑰輪替策略,無論是在 <X509Data>
區段或文件內容中。 使用此方法時,應用程式應該手動擷取憑證,並執行類似下列的驗證:
憑證是由憑證授權單位 (CA) 直接或透過鏈結發行,其公用憑證內嵌在應用程式中。
使用OS提供的信任清單,而不需進行其他檢查,例如已知的主體名稱,不足以防止竄改。SignedXml
憑證在文件簽署時已驗證為尚未過期(或「現在」進行近乎即時的文件處理)。
對於支持撤銷的 CA 所簽發的長期憑證,請確認憑證未撤銷。
憑證主體會驗證為適用於目前檔。
選擇轉換演算法
如果您要與指定特定值 (例如 XrML) 的規格互操作,則必須遵循規格。 如果您有封包簽章(例如簽署整個檔時),則需要使用 http://www.w3.org/2000/09/xmldsig#enveloped-signature (由 XmlDsigEnvelopedSignatureTransform 類別表示)。 您也可以指定隱含 XML-C14N 轉換,但並非必要。 對於包圍或中斷連結簽章,不需要任何轉換。 隱含 XML-C14N 轉換會處理所有專案。
隨著 Microsoft 安全性佈告欄 MS16-035 引進的安全性更新,.NET 已限制預設可在文件驗證中使用的轉換,且不受信任的轉換會導致CheckSignature一律傳回 false
。 特別是,由於惡意使用者容易濫用,因此不再允許需要額外輸入的轉換(指定為 XML 中的子元素)。 W3C 建議避免 XPath 和 XSLT 轉換,這是受這些限制影響的兩個主要轉換。
外部參考的問題
如果應用程式未驗證外部參考似乎適合目前的內容,則可以使用提供許多安全性弱點的方式濫用它們(包括阻斷服務、分散式 反思 拒絕服務、資訊洩漏、簽章略過和遠端程式代碼執行)。 即使應用程式要驗證外部參考 URI,仍會有兩次載入資源的問題:一次您的應用程式讀取它,一次讀取它 SignedXml 。 由於不保證應用程式讀取和文件驗證步驟的內容相同,因此簽章不會提供可信任性。
假設有外部參考的風險,當遇到外部參考時, SignedXml 將會擲回例外狀況。 如需此問題的詳細資訊,請參閱 知識庫文章3148821。