Aller au contenu

Dépannage : échecs de signature et d'horodatage

Ces fiches couvrent les échecs de signature que le moteur lève via NextPDF\Exception\SignatureException et NextPDF\Security\Signature\Exception\SignatureLevelUnreachableException. Chaque fiche indique la méthode de fabrique ou la classe exacte, afin que tu puisses confirmer la cause à partir du message et de getContext() plutôt que de la déduire.

Remarque de formulation : le moteur ne certifie ni qu’une signature est valide ni qu’un document est protégé. Il signale l’échec détecté. Considère chaque résolution comme une étape destinée à supprimer une cause signalée.

Entrée : le niveau PAdES « B-LT » ou « B-LTA » ne peut pas être produit

Section intitulée « Entrée : le niveau PAdES « B-LT » ou « B-LTA » ne peut pas être produit »
  • Symptôme. SignatureException dont le message se termine par nextpdf/enterprise package is required for B-LT/B-LTA signatures.
  • Cause probable. Le fournisseur de la capacité de validation à long terme manque. B-LT et B-LTA intègrent le matériel de révocation et un horodatage d’archivage ; ce fournisseur est inclus dans nextpdf/enterprise.
  • Preuve / diagnostic. La fabrique SignatureException::ltvCapabilityMissing() produit ce message exact. getContext() renvoie signature_level avec le niveau que tu as tenté.
  • Résolution.
    1. Installe le fournisseur : exécute composer require nextpdf/enterprise.
    2. Réexécute l’appel de signature.
    3. Si tu ne peux pas installer le fournisseur, demande plutôt B-B ou B-T, que le paquet cœur sait produire.
  • Voir aussi. Référence des exceptions.

Entrée : le niveau de signature est inatteignable et l’appel est rejeté

Section intitulée « Entrée : le niveau de signature est inatteignable et l’appel est rejeté »
  • Symptôme. SignatureLevelUnreachableException avec un message de la forme PAdES level "<x>" is unreachable (highest achievable: "<y>").
  • Cause probable. Le niveau de conformité demandé requiert une infrastructure indisponible au moment de la signature — le plus souvent une autorité d’horodatage pour B-T et au-delà. Le moteur échoue en mode fermé : il ne rétrograde pas silencieusement pour annoncer ensuite le niveau supérieur.
  • Preuve / diagnostic. getContext() renvoie requestedLevel, highestAchievableLevel et reason. Le champ reason indique l’élément d’infrastructure manquant. C’est le comportement par défaut en mode fermé, introduit pour empêcher qu’un document revendique un niveau qu’il n’atteint pas.
  • Résolution.
    1. Lis le champ reason pour identifier l’infrastructure manquante.
    2. Fournis le composant manquant — par exemple, configure une autorité d’horodatage — puis réexécute l’appel.
    3. Pour accepter délibérément un niveau inférieur, passe allowDegradation: true à PadesOrchestrator. L’appel produit alors highestAchievableLevel et signale le niveau qu’il a réellement produit.
  • Voir aussi. Chiffrement et permissions.

Entrée : le client de l’autorité d’horodatage est requis mais absent

Section intitulée « Entrée : le client de l’autorité d’horodatage est requis mais absent »
  • Symptôme. SignatureException se terminant par TSA client is required for level <x> but none was provided.
  • Cause probable. Une demande B-T, B-LT ou B-LTA requiert un client d’autorité d’horodatage, et aucun n’a été configuré dans l’orchestrateur.
  • Preuve / diagnostic. La fabrique SignatureException::tsaRequired() produit ce message ; getContext() contient le signature_level tenté.
  • Résolution.
    1. Configure un client d’autorité d’horodatage et passe-le à l’orchestrateur.
    2. Réexécute l’appel.
    3. Pour produire un niveau qui ne requiert pas d’horodatage, demande B-B.
  • Voir aussi. Référence des exceptions.

Entrée : l’URL du point de terminaison de l’autorité d’horodatage est vide

Section intitulée « Entrée : l’URL du point de terminaison de l’autorité d’horodatage est vide »
  • Symptôme. SignatureException se terminant par TSA endpoint URL is empty.
  • Cause probable. Un client d’autorité d’horodatage a été construit avec une URL de point de terminaison vide.
  • Preuve / diagnostic. La fabrique SignatureException::tsaUrlEmpty() produit ce message. Il s’agit d’un défaut de configuration, pas d’un échec réseau.
  • Résolution.
    1. Définis une URL de point de terminaison non vide pour le client d’autorité d’horodatage, par exemple https://timestamp.example.com/tsa.
    2. Si l’horodatage n’est pas requis pour le niveau demandé, retire plutôt le câblage du client d’autorité d’horodatage.
    3. Réexécute l’appel.
  • Voir aussi. Référence des exceptions.

Entrée : l’espace réservé de signature est absent du tampon

Section intitulée « Entrée : l’espace réservé de signature est absent du tampon »
  • Symptôme. SignatureException se terminant par no /Contents <…> field found in PDF buffer (signature placeholder missing).
  • Cause probable. L’étape de signature s’est exécutée sur un tampon qui ne contient aucun conteneur de signature réservé ; il n’y a donc aucun emplacement où écrire la signature.
  • Preuve / diagnostic. La fabrique SignatureException::signatureContentsNotFound() produit ce message.
  • Résolution.
    1. Assure-toi que le champ de signature et son espace réservé sont écrits avant l’exécution de l’étape de signature.
    2. Réexécute le pipeline pour que l’espace réservé soit présent au démarrage de la signature.
  • Voir aussi. Référence des exceptions.

Entrée : le statut de révocation est inconnu (le répondeur OCSP a refusé)

Section intitulée « Entrée : le statut de révocation est inconnu (le répondeur OCSP a refusé) »
  • Symptôme. SignatureException se terminant par OCSP responder returned non-successful OCSPResponseStatus "<status>".
  • Cause probable. Le répondeur OCSP n’a pas renvoyé de statut successful ; il n’a donc produit aucune assertion de révocation. Le moteur suit la RFC 6960 §4.2.1, citée dans le code source : un corps de réponse renseigné n’est autorisé que pour le statut successful (0). Le moteur refuse de traiter une réponse refusée comme un résultat de confiance positif.
  • Preuve / diagnostic. La fabrique SignatureException::nonSuccessfulOcspResponseStatus() produit ce message et indique le statut signalé, par exemple tryLater ou internalError. Un octet de statut réservé ou inconnu produit plutôt SignatureException::reservedOcspResponseStatus().
  • Résolution.
    1. Identifie le statut dans le message. Pour un statut transitoire tel que tryLater, réessaie la récupération de révocation plus tard.
    2. Pour unauthorized ou malformedRequest, vérifie l’URL de la requête OCSP et le certificat attendu par le répondeur.
    3. Ne masque pas l’échec d’obtention d’un artefact B-LT ou B-LTA ; l’assertion de révocation fait partie de ce niveau.
  • Voir aussi. Référence des exceptions.

Entrée : une entrée de la chaîne de certificats ne peut pas être décodée

Section intitulée « Entrée : une entrée de la chaîne de certificats ne peut pas être décodée »
  • Symptôme. SignatureException se terminant par failed to base64-decode PEM body — input is not valid PEM.
  • Cause probable. Une entrée de la chaîne de certificats n’est pas un PEM valide — typiquement une troncature, un caractère parasite ou un blob DER binaire fourni là où un PEM était attendu.
  • Preuve / diagnostic. La fabrique SignatureException::pemDecodingFailed() produit ce message lors de l’assemblage de la chaîne.
  • Résolution.
    1. Inspecte chaque certificat de la chaîne à la recherche de caractères parasites ou d’une troncature.
    2. Réexporte le certificat concerné au format PEM.
    3. Réexécute l’appel de signature.
  • Voir aussi. Chiffrement et permissions.

Entrée : le type de clé privée ne correspond pas à l’algorithme

Section intitulée « Entrée : le type de clé privée ne correspond pas à l’algorithme »
  • Symptôme. SignatureException se terminant par expected private key of type "<x>" for the configured algorithm but got "<y>".
  • Cause probable. La clé privée chargée ne correspond pas à l’algorithme de signature configuré — par exemple une clé RSA avec une sélection ECDSA.
  • Preuve / diagnostic. La fabrique SignatureException::unexpectedKeyType() produit ce message et nomme la classe de clé attendue ainsi que la classe réelle.
  • Résolution.
    1. Vérifie que le certificat et la paire de clés correspondent à l’algorithme que tu as sélectionné.
    2. Change la sélection d’algorithme pour l’aligner sur la clé, ou charge la clé qui correspond à l’algorithme.
    3. Réexécute l’appel de signature.
  • Voir aussi. Référence des exceptions.

Entrée : le matériel de clé ou de signature Ed25519 est malformé

Section intitulée « Entrée : le matériel de clé ou de signature Ed25519 est malformé »
  • Symptôme. SignatureException dont le message nomme une discordance de longueur Ed25519 — par exemple Ed25519 signature length <n> ≠ expected 64 bytes, ou Ed25519 round-trip self-verify failed.
  • Cause probable. La bibliothèque cryptographique compilée de l’environnement d’exécution a renvoyé du matériel de clé ou de signature d’une longueur incorrecte, ou une signature tout juste produite n’a pas pu être vérifiée avec sa propre clé publique. Le moteur cite la RFC 8032 §3.4 dans le code source, qui fixe une signature Ed25519 détachée à 64 octets. Le moteur s’interrompt plutôt que d’émettre du matériel qu’il ne peut pas vérifier lui-même.
  • Preuve / diagnostic. Les fabriques concernées sont SignatureException::ed25519SignatureMalformed(), ::ed25519RoundTripVerifyFailed(), ::ed25519KeyParseFailed(), ::ed25519SeedInvalid(), ::ed25519SecretKeyMalformed() et ::ed25519PublicKeyInvalid(). Chacune indique la longueur observée.
  • Résolution.
    1. Réinstalle l’extension PHP libsodium ; une compilation tronquée ou corrompue est la cause documentée d’un matériel de longueur incorrecte.
    2. Confirme que la clé est une clé Ed25519 et qu’OpenSSL est en version 1.1.1 ou plus récente.
    3. Réexécute l’appel de signature.
  • Voir aussi. Référence des exceptions.

Entrée : le dictionnaire d’horodatage d’archivage n’a pas été émis

Section intitulée « Entrée : le dictionnaire d’horodatage d’archivage n’a pas été émis »
  • Symptôme. SignatureException se terminant par no /Type /DocTimeStamp dictionary was emitted into the PDF buffer.
  • Cause probable. La boucle d’archivage B-LTA s’est exécutée, mais le dictionnaire d’horodatage du document n’a jamais été écrit dans le tampon, de sorte que l’artefact serait un B-LTA à moitié écrit. Le moteur refuse de le renvoyer.
  • Preuve / diagnostic. La fabrique SignatureException::documentTimestampNotEmitted() produit ce message. C’est un échec de postcondition levé au moment de la finalisation.
  • Résolution.
    1. Considère la sortie comme rejetée ; ne livre pas l’artefact partiel.
    2. Réexécute le pipeline B-LTA avec une autorité d’horodatage joignable.
    3. Si l’échec se répète, capture getContext() et l’exception précédente chaînée pour un rapport de défaut.
  • Voir aussi. Référence des exceptions.
  • Ces fabriques renseignent cert_info avec un sujet ou une empreinte uniquement lorsque cette information est disponible ; un cert_info vide est attendu pour les échecs de capacité et de configuration.
  • Une demande B-LT ou B-LTA sans client HTTP configuré lève SignatureException::httpClientMissing() — la récupération de révocation a besoin d’un client PSR-18 fourni à l’orchestrateur.
  • Un certificat adossé à un HSM sans implémentation de signataire lève SignatureException::hsmSignerMissing() ; raccorde le signataire au certificat avant de signer.

Glossaire : niveau PAdES · assertion de révocation