Inspect an existing signature and understand the trust boundary
At a glance
Section titled “At a glance”Use the Core inspector to detect whether a PDF has a signature, and keep the trust boundary clear. Detection is not verification. Premium editions or an external verifier handle cryptographic verification, trust-path validation, and revocation checking.
Prerequisites
Section titled “Prerequisites”- Core installed:
composer require nextpdf/core:^3. - A PDF file to inspect.
Recipe
Section titled “Recipe”- Read the PDF bytes.
- Create an
Inspectorand callinspect(). - Read
InspectResult::$hasSigned.truemeans the file contains a signature dictionary. - Read
InspectResult::$isEncryptedand the risk flags for surrounding context. - Route the file to a verifier for the cryptographic decision. The inspector reports presence, not validity.
The diagram below shows the boundary: presence is not validity. It also shows the checks a real verifier must still perform.
Full example
Section titled “Full example”<?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";}Expected output
Section titled “Expected output”For a signed input:
A signature is present in incoming.pdfEncrypted: noNext step: run a cryptographic verifier before trusting it.For an unsigned input:
No signature found in incoming.pdfEdge cases
Section titled “Edge cases”- Presence is not validity — boundary.
InspectResult::$hasSignedreports that a signature dictionary exists in the file. It does not check the Cryptographic Message Syntax (CMS) structure, the byte-range digest, the signing certificate, the certificate chain, or revocation status. A tampered file can still reporthasSigned = true. Never treat presence as proof of integrity or authorship. - What full verification needs. A complete decision recomputes the byte-range digest and compares it (ISO 32000-2 §12.8.1), validates the CMS SignedData, builds and checks the X.509 path to a trusted anchor, and checks revocation through Online Certificate Status Protocol (OCSP) or a certificate revocation list (CRL). For long-term inputs, validation data lives in the Document Security Store (DSS) (ETSI EN 319 142-2 §6.3.1). These operations run behind the
SignerInterfaceandLtvManagerInterfacecontracts; the production implementations ship in the Pro and Enterprise editions. An external validator is the other supported path. - Inspection depth and the sidecar (SIDECAR-001). A default
new Inspector()has no Spectrum sidecar configured. Without a sidecar, onlyInspectDepth::Quickreturns a result.InspectDepth::Quickuses the in-process PHP fallback.InspectDepth::StandardandInspectDepth::Fullboth require the sidecar. If no sidecar is available, they throwInspectExceptionwith codeINSPECT-SIDECAR-001(“Spectrum sidecar is required for Standard/Full depth inspection”,retryable = true). TheInspectConfigconstructor’s default depth isStandard. Therefore, a barenew InspectConfig()(ornew InspectConfig(depth: InspectDepth::Standard)) is not offline-usable. It throws SIDECAR-001 beforehasSignedis ever read. UseInspectConfig::quick()for offline presence detection, as this recipe does. No depth performs cryptographic signature verification in Core. - Empty input. An empty string throws
InspectExceptionwith codeINSPECT-INPUT-001. Guard the file read. - Multiple signatures. The presence flag does not count signatures or distinguish an approval signature from a document timestamp. Use a dedicated verifier when the count or per-signature verdict matters.
Conformance
Section titled “Conformance”| Statement | Spec | Clause | reference_id |
|---|---|---|---|
The signature value is stored in the signature dictionary Contents entry. | ISO 32000-2 | §12.8.1 | |
Verification recomputes the digest over the ByteRange, excluding the signature value. | ISO 32000-2 | §12.8.1 | |
| Long-term validation data lives in the DSS. | ETSI EN 319 142-2 | §6.3.1 |
This recipe detects whether a signature is present. It does not assert that any signature is valid, trusted, or unrevoked. That decision belongs to a cryptographic verifier.