Aller au contenu

Inspecter une signature existante et comprendre la frontière de confiance

Cette recette utilise l’inspecteur Core pour détecter si un PDF contient une signature, puis pose la frontière de confiance. Détecter une signature ne revient pas à la vérifier. La vérification cryptographique, la validation du chemin de confiance et le contrôle de révocation relèvent de Premium ou d’un outil externe.

  • Core installé : composer require nextpdf/core:^3.
  • Un fichier PDF à inspecter.
  1. Lis les octets du PDF.
  2. Crée un Inspector et appelle inspect().
  3. Lis InspectResult::$hasSigned. true signifie qu’un dictionnaire de signature est présent dans le fichier.
  4. Lis InspectResult::$isEncrypted et les indicateurs de risque pour situer le contexte.
  5. Transmets le fichier à un vérificateur pour obtenir la décision cryptographique. L’inspecteur signale une présence, pas une validité.

Le schéma ci-dessous met en évidence deux points : la frontière que cette recette rend explicite, la présence n’est pas la validité ; et l’ensemble complet des contrôles qu’une véritable vérification doit encore effectuer.

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

Pour une entrée signée :

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

Pour une entrée non signée :

No signature found in incoming.pdf
  • La présence n’est pas la validité — frontière. InspectResult::$hasSigned indique qu’un dictionnaire de signature existe dans le fichier. Il ne vérifie ni la structure CMS, ni le condensat de la plage d’octets, ni le certificat de signature, ni la chaîne de certification, ni l’état de révocation. Un fichier altéré peut tout de même signaler hasSigned = true. Ne traite jamais la présence comme une preuve d’intégrité ou de paternité.
  • Ce qu’exige une vérification complète. Pour prendre une décision complète, le vérificateur recalcule le condensat de la plage d’octets et le compare (ISO 32000-2 §12.8.1), valide le CMS SignedData, construit et vérifie le chemin X.509 jusqu’à un ancrage de confiance, puis contrôle la révocation via OCSP ou CRL. Pour les entrées à long terme, les données de validation résident dans le DSS (ETSI EN 319 142-2 §6.3.1). Ces opérations passent par les contrats SignerInterface et LtvManagerInterface ; les implémentations de production sont livrées dans les éditions Pro et Enterprise. L’autre chemin pris en charge consiste à utiliser un validateur externe.
  • Profondeur d’inspection et sidecar (SIDECAR-001). Par défaut, un new Inspector() n’a aucun sidecar Spectrum configuré. Sans sidecar, seul InspectDepth::Quick renvoie un résultat. InspectDepth::Quick utilise le repli PHP dans le processus courant. InspectDepth::Standard et InspectDepth::Full requièrent tous deux le sidecar. En l’absence de sidecar, ils lèvent une InspectException avec le code INSPECT-SIDECAR-001 (“Spectrum sidecar is required for Standard/Full depth inspection”, retryable = true). Le constructeur InspectConfig a pour profondeur par défaut Standard. Par conséquent, un simple new InspectConfig() (ou new InspectConfig(depth: InspectDepth::Standard)) n’est pas utilisable hors ligne. Il lève SIDECAR-001 avant même que hasSigned ne soit lu. Utilise InspectConfig::quick() pour la détection de présence hors ligne, comme dans cette recette. Aucune des profondeurs n’effectue de vérification cryptographique de signature dans Core.
  • Entrée vide. Une chaîne vide lève une InspectException avec le code INSPECT-INPUT-001. Protège l’étape de lecture.
  • Signatures multiples. L’indicateur de présence ne compte pas les signatures et ne distingue pas une signature d’approbation d’un horodatage de document. Utilise un vérificateur dédié quand le nombre de signatures ou le verdict par signature a de l’importance.
ÉnoncéSpécificationClausereference_id
La valeur de signature est stockée dans l’entrée Contents du dictionnaire de signature.ISO 32000-2§12.8.1
La vérification recalcule le condensat sur le ByteRange, en excluant la valeur de signature.ISO 32000-2§12.8.1
Les données de validation à long terme résident dans le DSS.ETSI EN 319 142-2§6.3.1

Cette recette détecte une signature. Elle n’établit pas que la signature est valide, de confiance ou non révoquée. Cette décision revient à un vérificateur cryptographique.