跳转到内容

检查现有签章,理解信任边界

本 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 只检测签章是否存在。它不会主张任何签章有效、受信任或未被撤销。该判定属于密码学验证器的职责。