共用方式為


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.Urinull,表示接收應用程式應該知道物件的身分識別。 在大部分情況下, 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> 值及其相關聯的轉換之前先驗證計算,並會提早中止,以避免潛在的惡意處理指令。

選擇要簽署的專案

建議您盡可能使用 屬性的 「值(或將 屬性設定UriURI空字串)。 這表示會將整份文件視為摘要計算,這表示整個檔會受到保護,以免遭到竄改。

以錨點的形式查看 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