跳到內容

檢查既有簽章並釐清信任邊界

這則 recipe(範例)會用 Core inspector 偵測一份 PDF 是否帶有簽章,並劃清邊界。偵測到簽章並不代表已完成驗證。密碼學驗證、信任路徑驗證與撤銷檢查屬於 Premium 或外部範疇。

  • 已安裝 Core:composer require nextpdf/core:^3
  • 一份要檢查的 PDF 檔。
  1. 讀取 PDF 位元組。
  2. 建立一個 Inspector 並呼叫 inspect()
  3. 讀取 InspectResult::$hasSignedtrue 表示檔案中存在簽章字典。
  4. 讀取 InspectResult::$isEncrypted 與風險旗標,作為背景脈絡。
  5. 將檔案交給驗證器,由驗證器做出密碼學判定。inspector 回報的是存在性,而非有效性。

下方圖示呈現兩件事:本則 recipe 釐清的邊界,也就是存在不等於有效;以及真正驗證仍須完成的完整檢查項目。

false

true

Yes

No

Yes

No / unknown

Incoming PDF

Inspector.inspect — Quick

hasSigned?

No signature present

Signature dictionary PRESENT

NOT yet validity

Recompute byte-range digest

ISO 32000-2 §12.8.1

Validate CMS SignedData

Build & check X.509 path

to a chosen trust anchor

Check revocation OCSP / CRL

Long-term input?

Read DSS validation material

All checks evaluated

Every check passed?

Trustworthy

Not trusted — do not act

Diagram
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Inspect\Inspector;
use NextPDF\Inspect\InspectConfig;
$pdfData = file_get_contents(__DIR__ . '/incoming.pdf');
if ($pdfData === false || $pdfData === '') {
fwrite(STDERR, "Cannot read incoming.pdf\n");
exit(1);
}
$inspector = new Inspector();
// InspectConfig::quick() selects InspectDepth::Quick. This is the only
// depth that runs offline: a default Inspector has no Spectrum sidecar,
// and without a sidecar Quick is the sole path that returns a result.
// Standard and Full require the sidecar (see the SIDECAR-001 edge case).
$result = $inspector->inspect(
$pdfData,
InspectConfig::quick(),
);
// hasSigned reports the PRESENCE of a signature dictionary.
// It does NOT mean the signature verifies.
if ($result->hasSigned) {
echo "A signature is present in incoming.pdf\n";
echo "Encrypted: " . ($result->isEncrypted ? 'yes' : 'no') . "\n";
echo "Next step: run a cryptographic verifier before trusting it.\n";
} else {
echo "No signature found in incoming.pdf\n";
}

對於已簽署的輸入:

A signature is present in incoming.pdf
Encrypted: no
Next step: run a cryptographic verifier before trusting it.

對於未簽署的輸入:

No signature found in incoming.pdf
  • 存在不等於有效 — 邊界。 InspectResult::$hasSigned 只會回報檔案中存在一個簽章字典。它不會檢查 CMS 結構、byte-range 摘要、簽署憑證、憑證鏈或撤銷狀態。遭竄改的檔案仍可能回報 hasSigned = true。絕不可把存在性當成完整性或作者身分的證明。
  • 完整驗證需要什麼。 一次完整判定會重新計算 byte-range 摘要並加以比對(ISO 32000-2 §12.8.1),驗證 CMS SignedData,建立並檢查通往受信任錨點的 X.509 路徑,並透過 OCSP 或 CRL 檢查撤銷。對於長期保存的輸入,驗證資料存放在 DSS 中(ETSI EN 319 142-2 §6.3.1)。這些操作由 SignerInterfaceLtvManagerInterface 契約背後的實作執行;正式環境實作隨 Pro 與 Enterprise 版本提供。外部驗證器則是另一條受支援的路徑。
  • 檢查深度與 sidecar(旁路服務,SIDECAR-001)。 預設的 new Inspector() 不會設定 Spectrum sidecar。沒有 sidecar 時,只有 InspectDepth::Quick 會回傳結果。InspectDepth::Quick 會使用行程內 PHP 後援路徑。InspectDepth::StandardInspectDepth::Full 都需要 sidecar。若沒有可用的 sidecar,它們會丟出 InspectException,代碼為 INSPECT-SIDECAR-001(「Spectrum sidecar is required for Standard/Full depth inspection」,retryable = true)。InspectConfig 建構式的 預設深度是 Standard。因此,沒帶參數的 new InspectConfig()(或 new InspectConfig(depth: InspectDepth::Standard)無法離線使用。它會在讀取 hasSigned 之前就丟出 SIDECAR-001。請改用 InspectConfig::quick() 進行離線存在性偵測,就像本則 recipe 所示。Core 中沒有任何一種深度會執行密碼學上的簽章驗證。
  • 空白輸入。 空字串會丟出 InspectException,代碼為 INSPECT-INPUT-001。請先防護讀取結果。
  • 多個簽章。 存在旗標不會計算簽章數量,也不會區分核可簽章與文件時間戳記。當簽章數量或逐一簽章的判定結果重要時,請使用專用驗證器。
陳述規範條款參考 ID
簽章值存放在簽章字典的 Contents 項目中。ISO 32000-2§12.8.1
驗證會在 ByteRange 上重新計算摘要,並排除簽章值本身。ISO 32000-2§12.8.1
長期驗證資料存放在 DSS 中。ETSI EN 319 142-2§6.3.1

本則 recipe 只偵測簽章。它不會主張任何簽章有效、受信任或未撤銷。該判定屬於密碼學驗證器的職責。