正確驗證簽章
Spec: RFC 5280, §6 RFC 5280 §6 Spec: RFC 6960 RFC 6960 Spec: RFC 5652 RFC 5652 Evidence: Test-backed
「簽章有效」通常只代表檢查了一件事:數學算式對得上。一次正確的驗證至少要檢查五件彼此獨立的事;其中任何一件出錯,都可能讓那個綠色勾號變得毫無意義。本頁列出完整檢查項目,並說明為什麼不完整的答案很危險。
為什麼這很重要
標題為「為什麼這很重要」的區段在這個主題裡,單一布林值是最危險的輸出。它會讓讀者把「有效」當成「值得信任」,然而「有效」可能只代表那些位元組未經竄改——而簽署所用的金鑰,卻來自一張三年前就過期、上個月遭到撤銷、且鏈結不到任何你認可之機構的憑證。上述每一項都是一個獨立的檢查。回報單一布林值的軟體,已經默默替你決定哪些檢查才重要,而且這個決定不是由你做出。在受監管或具契約效力的場合,如果工具只驗證了成本最低的那項屬性,「工具說它有效」並不能作為辯護。
簡短版本
標題為「簡短版本」的區段一次完整的驗證會回答五個各自獨立的問題。它們彼此獨立:通過其中一項,並不代表其他項也成立:
- 完整性——已簽署的位元組經雜湊後,是否仍與簽章所涵蓋的值相符?(重新計算位元組範圍摘要,並加以比對。)
- 真實性——密碼學簽章是否能以簽署憑證中的公開金鑰,針對已簽署屬性完成驗證?
- 憑證路徑——該憑證是否鏈結到你所選定的信任錨點,且每一個環節都有效?
- 時間——在相關時間點,憑證是否仍在有效期間內,而那個時間是否屬於受信任的時間,而不是自行宣稱的時間?
- 撤銷——在當時,憑證是否未遭撤銷,且這項證據(OCSP/CRL)是否可由你實際取得,或已內嵌於文件中?
一個未執行全部五項檢查的「有效」,只是看起來像完整答案的不完整答案。
NextPDF 的處理方式
標題為「NextPDF 的處理方式」的區段NextPDF 的立場是:每一個問題都各自獨立,且每一個都必須被明確回答。NextPDF 絕不把一個問題壓成單一的樂觀旗標,也絕不因為某項檢查不方便而默默略過。這一點由測試強制約束。這正是本頁被標示為由測試背書(test-backed)、而非由標準背書(standard-backed)的原因:這項行為由測試套件鎖定,而不只是從某條條款論證而來。
完整性與真實性都有端對端測試。測試套件會使用對應於一張已知憑證的金鑰,對一個真實的已簽署屬性結構進行簽署,再以相符的公開金鑰,跨多個時間向量驗證該簽章。因此,任何破壞此正規結構的變更都會讓測試失敗。憑證路徑驗證由一組測試鎖定:它們刻意竄改一個簽章位元組,並斷言結果為非有效,且附帶一個結構化的原因——不是被丟棄的例外,而是一筆明確記錄的失敗。時間戳記符記的驗證被拆解成數個離散步驟——解碼、簽署者資訊、已簽署屬性、訊息摘要、憑證綁定、金鑰用途、簽章、產生時間(produced-at)——而每一個步驟都各自接受測試,因此「時間戳記通過驗證」意味著每一個步驟都通過了驗證。撤銷檢查的軟性失敗(無法連線的回應端)在程式碼與測試中,都會與明確的「已撤銷」區分開來。這兩者絕不會被混為同一個答案。
- Integrity Recompute the byte-range digest and compare it to the value the signature covers.
- Authenticity Verify the cryptographic signature against the certificate’s public key, over the signed attributes — not the raw content.
- Certificate path Build and validate the chain to a trust anchor you chose; every link’s signature, validity, and constraints must hold.
- Time Confirm the certificate was valid at the relevant instant, and that the instant is trusted time, not the signer’s clock.
- Revocation Confirm the certificate was not revoked at that time, using obtainable or embedded OCSP/CRL evidence.
證據怎麼說
標題為「證據怎麼說」的區段Evidence: Test-backed 這項行為以測試為依據,而這些測試實作了各項標準所要求的內容。
完整性對應 Spec: ISO 32000-2, §12.8.1 ISO 32000-2 §12.8.1 :摘要會針對位元組範圍重新計算,並與儲存的值比對,任何差異都意味著簽章無效。針對已簽署屬性的真實性,由一項整合測試涵蓋:它會對一組真實的已簽署屬性進行簽署,並以相符的公開金鑰跨多個時間向量加以驗證。憑證路徑這個問題對應
Spec: RFC 5280, §6.1 RFC 5280 §6.1 :有效的路徑始於一個信任錨點,而 Spec: RFC 5280, §6.2 RFC 5280 §6.2 指出,該演算法定義了一條路徑要被視為有效所需的最低條件——一項路徑驗證器單元測試會斷言:一個被竄改的簽章會產生 valid = false,並附帶一個明確的原因,絕不會默默接受。
撤銷的檢查順序對應 Spec: RFC 6960, §3.2 RFC 6960 §3.2 :在用戶端接受一個已簽署的撤銷回應為有效之前,它「應當」(SHALL)先確認該回應自身的簽章為有效,且簽署者目前已獲授權——而 Spec: RFC 6960, §4.2.2.2 RFC 6960 §4.2.2.2 將該授權定義為由相關 CA 直接簽發的 id-kp-OCSPSigning 委派授權。因此,一個尚未確認由已獲授權且可驗證之簽署者簽出的撤銷答案,是毫無意義的。憑證綁定檢查對應 Spec: RFC 5035, §5.4.2 RFC 5035 §5.4.2 :如果已簽署之 signing-certificate-v2 屬性中的憑證雜湊,與用來驗證簽章的憑證不相符,那麼該簽章必須被視為無效。這會封住替換漏洞——也就是簽章針對一張由攻擊者選定的憑證通過驗證的情況。時間戳記符記本身,會以 Spec: RFC 5652 RFC 5652 的風格,作為一個 CMS 物件逐步驗證,每一個步驟都各自接受測試。
實務範例
標題為「實務範例」的區段真正有啟發性的,不是某一次 API 呼叫,而是在你依據某個結果採取行動之前,你必須有能力回答的那些問題。請把它當成審查時會用來檢核你的清單。
<?php
declare(strict_types=1);
// A correct validation produces a structured outcome, not one boolean.// Before you trust a signature, you must be able to answer ALL of these://// integrity : Does the byte-range digest still match? (tamper check)// authenticity: Does the signature verify over the SIGNED ATTRIBUTES,// not just the content?// path : Does the certificate chain to a trust anchor YOU chose,// with every link valid at the relevant time?// time : Is the relevant time TRUSTED (a timestamp), or merely the// signer's self-asserted clock?// revocation : Was the certificate not revoked at that time, by evidence// you obtained or that the document embedded?//// "valid: true" without an answer to every line above is an incomplete// result. A path-validation outcome carries a `valid` flag AND a structured// `reasons` list precisely so a failure says WHY — never a bare false.如果任何一行的答案是「我不知道」,那麼如實的狀態就不是「有效」。它是「尚未確定」——而把兩者混為一談,正是本頁要防止的錯誤。
常見誤解
標題為「常見誤解」的區段陷阱在於把「密碼學上有效」等同於「值得信任」。完整性與真實性合在一起,只能證明這些位元組是由持有這把金鑰的人所簽署。它們對於這把金鑰的憑證是否受信任、是否仍在有效期、或是否未遭撤銷,都隻字未提。一份以自行產生的憑證簽署的文件,可以是「密碼學上有效」卻一文不值。另一個反向陷阱,則是把一個無法確定的撤銷檢查(回應端離線)當成通過——或當成失敗。兩者都不是。它是未知的,而一個正確的驗證器會如實回報它為未知,而不是往任一方向猜測。一個沒有說清楚五項檢查中實際執行了哪些的綠色勾號,並不是驗證結果。它是別人替你做出的決定。
限制與邊界
標題為「限制與邊界」的區段NextPDF 會執行並測試結構性與密碼學檢查。它不會替你選擇信任錨點,也不保證建立在其上的策略。你要信任哪些憑證,是引擎無法替你做出的部署決策。一條驗證到某個你本不該信任之錨點的鏈,仍然是你無法仰賴的驗證結果。撤銷證據唯有在可取得或已內嵌時才能被檢查。一個離線的回應端會產生「無法確定」,而把它轉換成裁決結果是一項策略選擇,而非引擎的選擇。本頁描述的是檢查項目,而非法律上的充分性。一個通過驗證的簽章是否具有某種特定的法律效力,取決於憑證、簽署者、司法管轄區,以及相應的義務。內嵌證據如何讓這些檢查在多年後仍能被回答,在 長期驗證 中有所說明;完整性檢查背後的位元組範圍機制,則收錄於 簽章如何置於 PDF 中。
驗證介面的方案版本可用性:
| Edition | Availability |
|---|---|
| Core | 已簽署屬性的完整性與真實性,加上 RFC 5280 §6 對所提供信任錨點執行的憑證路徑驗證。 |
| Pro | 新增 RFC 3161 時間戳記符記驗證,也就是將受信任時間這個問題拆解為可獨立檢查的多個步驟。 |
| Enterprise | 新增撤銷評估(OCSP/CRL),以及針對內嵌長期素材的驗證,並區分無法確定的結果與明確結果。 |
相關文件
標題為「相關文件」的區段- 長期驗證——內嵌證據如何讓時間與撤銷檢查在多年後仍能被回答。
- 簽章如何置於 PDF 中——完整性檢查所重新計算的位元組範圍機制。
- 時間戳記與受信任時間——是什麼讓「時間」這個問題得以不依賴簽署者時鐘來回答。
詞彙表
標題為「詞彙表」的區段- 完整性檢查——重新計算位元組範圍摘要,並將其與簽章所涵蓋的值比對。
- 真實性檢查——以簽署憑證的公開金鑰,針對已簽署屬性驗證該密碼學簽章。
- 已簽署屬性——簽章實際據以計算的、經過鑑別的 CMS 屬性(content-type、message-digest、signing-time、signing-certificate-v2)。
- 憑證路徑驗證——建立並檢查從簽署憑證到所選信任錨點的鏈(RFC 5280 §6)。
- 信任錨點——一個你決定要信任的憑證授權單位;可接受路徑的根。
- 撤銷檢查——透過 OCSP 或 CRL,判定一張憑證在相關時間點是否曾遭撤銷。
- 無法確定——既非「良好」也非「已撤銷」的撤銷結果,因為證據無法取得;既不算通過,也不算失敗。