本文提供此 API 參考文件的補充備註。
類別 SignedXml 是萬維網聯合會 (W3C) XML 簽章語法和處理規格的 .NET 實作,也稱為 XMLDSIG (XML 數位簽名)。 XMLDSIG 是以標準為基礎的互通方式,用來簽署和驗證 XML 檔的所有或部分或其他可從統一資源標識碼 (URI) 尋址的數據。
SignedXml每當您需要以標準方式在應用程式或組織之間共用已簽署的 XML 數據時,請使用 類別。 使用這個類別簽署的任何資料,都可以透過符合 W3C XMLDSIG 規範的實作來驗證。
類別 SignedXml 可讓您建立下列三種 XML 數字簽名:
| 簽章類型 | 說明 |
|---|---|
| 信封簽章 | 簽章包含在要簽署的 XML 元素內。 |
| 包封簽章 | 簽名的 XML 包含在 <Signature> 元素內。 |
| 內部分離式簽章 | 簽章和已簽署的 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>指定將
Signature元素從 XML/text 轉換為位元組以進行簽章驗證的規則。 .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,表示接收應用程式應該知道物件的身分識別。 在大部分情況下,nullURI 會導致丟出例外狀況。 除非您的應用程式與需要 URI 的通訊協定互通,否則請勿使用nullURI。將
URI屬性設定為空字串,表示檔的根元素正在簽署,這是一種信封簽章形式。如果屬性值以
URI#开头,則值必須解析為目前檔中的 元素。 此表單可以使用任何支援的簽章類型,包括信封簽章、封裝簽章或內部獨立簽章。任何其他項目都會被視為外部資源的分離簽章,且 SignedXml 類別不支援它。
元素
<Transforms>包含已排序的
<Transform>元素清單,描述簽署者如何取得已經摘要的數據物件。 轉換演算法類似於標準化方法,但與其重寫<Signature>元素,不如說重寫URI元素的<Reference>屬性所識別的內容。 元素<Transforms>由類別 TransformChain 表示。每個轉換演算法都會定義為接受 XML(XPath 節點集)或位元組作為輸入。 如果目前數據的格式與轉換輸入需求不同,則會套用轉換規則。
每個轉換演算法都會定義為產生 XML 或位元組作為輸出。
如果最後一個轉換演算法的輸出不是以位元組定義(或未指定任何轉換),則 標準化方法 會當做隱含轉換使用(即使元素中
<CanonicalizationMethod>指定了不同的演算法也一樣)。轉換演算法的值 http://www.w3.org/2000/09/xmldsig#enveloped-signature 會編碼一個規則,該規則會被解讀為從文件中移除
<Signature>元素。 否則,封裝簽章的驗證器會對文件,包括簽章,生成摘要,但簽署者在套用簽章之前就已對文件生成摘要,因此會導致不同的結果。
元素
<DigestMethod>識別摘要(加密哈希)方法,以應用於由
URI元素的<Reference>屬性識別的已轉換內容。 這是由 Reference.DigestMethod 屬性表示。
選擇標準化方法
除非與需要使用不同值的規格互作,否則我們建議您使用預設的 .NET 標準化方法,也就是 1.0 演演算法 XML-C14N,其值為 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,指向的是其 ID 屬性為 “foo” 的元素。 不幸的是,這很容易遭到竄改,因為這僅包含目標元素的內容,而不包括上下文。 濫用此區別稱為 XML 簽章包裝 (XSW)。
如果您的應用程式將批注視為語意(在處理 XML 時並不常見),則您應該使用 “#xpointer(/)” 而不是 “”,而 “#xpointer(id('foo'))”而不是 “#foo”。 #xpointer 版本會解釋為包含註解,而 shortname 形式則排除註解。
如果您需要接受只有部分受到保護的文檔,而且您想要確保您正在讀取的內容與簽署保護的內容相同,請使用 GetIdElement 方法。
KeyInfo 元素的安全性考慮
選擇性 <KeyInfo> 項目中的數據(也就是 KeyInfo 屬性),其中包含用來驗證簽章的索引鍵,不應受信任。
特別是當 KeyInfo 數值代表裸露的 RSA、DSA 或 ECDSA 公鑰時,文件可能會遭到竄改,即使 CheckSignature 方法報告簽章有效。 這可能會發生,因為執行竄改的實體只需要產生新的密鑰,並使用該新密鑰重新簽署竄改的檔。 因此,除非您的應用程式確認公鑰是預期的值,否則應該將檔視為遭到竄改。 這需要您的應用程式檢查內嵌在檔案中的公鑰,並針對檔案內容的已知值清單進行驗證。 例如,如果可以理解這份文件是由已知用戶發出,您就會對照該使用者所使用的已知密鑰清單檢查密鑰。
您也可以使用 CheckSignatureReturningKey 方法來處理文件之後驗證索引鍵,而不是使用 CheckSignature 方法。 但是,為了獲得最佳安全性,您應該事先確認密鑰。
或者,可以考慮嘗試用使用者的已註冊公鑰,而不是讀取元素中的內容<KeyInfo>。
關於 X509Data 元素的安全考量
選擇性的 <X509Data> 元素是 <KeyInfo> 元素的子系,並包含一或多個 X509 憑證或 X509 憑證的標識碼。 元素中的數據 <X509Data> 也不應該原本就受信任。
使用內嵌 <X509Data> 元素驗證檔時,.NET 只會驗證數據解析為 X509 憑證,其公鑰可以成功用來驗證檔簽章。 與呼叫 CheckSignature 方法並將參數設為 verifySignatureOnly 之情形不同,此處不進行撤銷檢查,也不檢查鏈結信任或驗證到期日。 即使您的應用程式擷取憑證本身,並將它 CheckSignature 傳遞給方法 verifySignatureOnly,並將參數設定為 false,這仍然不足以做為防止文件竄改的驗證。 憑證仍需要驗證是否適合要簽署的文件。
使用內嵌簽署憑證可以提供實用的密鑰輪替策略,無論是在 <X509Data> 區段或文件內容中。 使用此方法時,應用程式應該手動擷取憑證,並執行類似下列的驗證:
憑證是由憑證授權單位 (CA) 直接或透過鏈結發行,其公用憑證內嵌在應用程式中。
使用操作系統提供的信任清單,在不進行其他檢查(例如已知的主體名稱)情況下,無法足夠防止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。