Salta ai contenuti

Esaminare una firma esistente e delimitarne il confine di attendibilità

Questa ricetta usa l’inspector di Core per rilevare se un PDF contiene una firma e ne delinea il confine di attendibilità. Rilevare una firma non significa verificarla. La verifica crittografica, la convalida del percorso di attendibilità e il controllo della revoca sono funzionalità Premium o esterne.

  • Core installato: composer require nextpdf/core:^3.
  • Un file PDF da esaminare.
  1. Leggere i byte del PDF.
  2. Creare un Inspector e chiamare inspect().
  3. Leggere InspectResult::$hasSigned. true indica che nel file è presente un dizionario di firma.
  4. Leggere InspectResult::$isEncrypted e i flag di rischio per il contesto circostante.
  5. Inviare il file a un verificatore per la decisione crittografica. L’inspector segnala la presenza, non la validità.

Il diagramma seguente mostra due aspetti: il confine che questa ricetta rende esplicito, cioè la presenza non è validità, e l’insieme completo dei controlli che una verifica reale deve comunque eseguire.

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";
}

Per un input firmato:

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

Per un input non firmato:

No signature found in incoming.pdf
  • La presenza non è validità — confine. InspectResult::$hasSigned indica che nel file è presente un dizionario di firma. Non verifica la struttura CMS, il digest del byte-range, il certificato di firma, la catena di certificati né lo stato di revoca. Un file manomesso può comunque restituire hasSigned = true. Non considerare mai la presenza come prova di integrità o paternità.
  • Cosa richiede una verifica completa. Una verifica completa richiede di ricalcolare il digest del byte-range e confrontarlo (ISO 32000-2 §12.8.1), convalidare il SignedData CMS, costruire e verificare il percorso X.509 fino a un’ancora attendibile e controllare la revoca tramite OCSP o CRL. Per gli input a lungo termine, i dati di convalida risiedono nel DSS (ETSI EN 319 142-2 §6.3.1). Queste operazioni sono esposte dai contratti SignerInterface e LtvManagerInterface; le implementazioni di produzione sono incluse nelle edizioni Pro ed Enterprise. L’altro percorso supportato è un validatore esterno.
  • Profondità di ispezione e sidecar (SIDECAR-001). Un new Inspector() creato con la configurazione predefinita non ha alcun sidecar Spectrum configurato. Senza un sidecar, solo InspectDepth::Quick restituisce un risultato. InspectDepth::Quick usa il fallback PHP in-process. InspectDepth::Standard e InspectDepth::Full richiedono entrambi il sidecar. Se non è disponibile alcun sidecar, generano InspectException con codice INSPECT-SIDECAR-001 (“Spectrum sidecar is required for Standard/Full depth inspection”, retryable = true). Il costruttore di InspectConfig ha come profondità predefinita Standard. Pertanto un semplice new InspectConfig() (o new InspectConfig(depth: InspectDepth::Standard)) non è utilizzabile offline. Genera SIDECAR-001 prima ancora che hasSigned venga letto. Usare InspectConfig::quick() per rilevare la presenza offline, come in questa ricetta. Nessuna delle profondità esegue la verifica crittografica della firma in Core.
  • Input vuoto. Una stringa vuota genera InspectException con codice INSPECT-INPUT-001. Controllare la lettura.
  • Firme multiple. Il flag di presenza non conta le firme né distingue una firma di approvazione da una marca temporale del documento. Usare un verificatore dedicato quando il conteggio o il verdetto per ogni firma è rilevante.
DichiarazioneSpecificaClausolareference_id
Il valore della firma è memorizzato nella voce Contents del dizionario di firma.ISO 32000-2§12.8.1
La verifica ricalcola il digest sul ByteRange, escludendo il valore della firma.ISO 32000-2§12.8.1
I dati di convalida a lungo termine risiedono nel DSS.ETSI EN 319 142-2§6.3.1

Questa ricetta si limita a rilevare una firma. Non garantisce che una firma sia valida, attendibile o non revocata. Tale decisione spetta a un verificatore crittografico.