Un PDF que vous venez de signer est une signature B-B et rien de plus. Elle prouve qui a signé et que les octets n'ont pas bougé, mais elle ne porte aucune preuve que le certificat du signataire était valide au moment de la signature, si bien qu'un validateur, des années plus tard, doit aller chercher des données de révocation qui n'existent peut-être plus. Combler cet écart signifie écrire des réponses OCSP et des CRL dans le Document Security Store au niveau du document, et dans HotPDF c'est un appel : PopulatePAdESLTVEvidence parcourt chaque signature chargée, dérive les demandes de révocation de l'ensemble de certificats, les exécute via un transport que vous fournissez, et écrit le matériel récupéré plus la chaîne CMS dans le DSS. Elle renvoie le nombre de signatures dont la preuve a atterri, ou moins un quand le document n'a aucun champ de signature du tout
La décision de conception qui vaut la peine d'être comprise avant de l'utiliser est que la bibliothèque n'ouvre jamais de socket. Chaque octet qui arrive du réseau arrive par un callback que vous avez écrit. Ce n'est pas de la prudence pour elle-même ; c'est la seule façon pour cette fonctionnalité de fonctionner dans les environnements qui exigent réellement la validation à long terme
Pourquoi la bibliothèque refuse-t-elle de faire son propre HTTP ?
Parce que les endroits qui exigent des signatures B-LT sont les endroits où une bibliothèque ne peut pas se voir confier le réseau. Les services de signature tournent derrière des proxys authentifiants avec des racines d'entreprise. Les étages de signature isolés du réseau n'ont aucune route vers un répondeur et doivent être alimentés en preuves en cache. Les régimes d'audit exigent que chaque requête sortante soit journalisée par l'application, pas enterrée dans une dépendance. Et les suites de tests ont besoin de réponses déterministes, ce qui est impossible si la bibliothèque appelle toute seule
Le transport est une simple référence de fonction de forme fixe, donc la politique reste la vôtre. HotPDF vous remet un enregistrement de requête décrivant exactement quoi récupérer, y compris le type de contenu et un plafond de taille de réponse, et vous renvoyez les octets plus un statut
function FetchEvidence(const Request: THPDFSignatureEvidenceRequest;
Attempt: Integer; CancellationToken: THPDFCancellationToken;
out Response: TBytes; out RetryAfterMS: Cardinal;
out ErrorMessage: UnicodeString): THPDFSignatureEvidenceTransportStatus;
begin
RetryAfterMS := 0;
try
// Request.Kind dit si ceci est un POST OCSP ou un GET CRL ;
// Request.ContentType et Request.Body sont déjà préparés,
// et Request.MaxResponseBytes est le plafond que vous devez honorer
Response := HttpExchange(Request.URI, Request.ContentType,
Request.Body, Request.MaxResponseBytes);
Result := setsSucceeded;
except
on E: Exception do
begin
ErrorMessage := E.Message;
// setsRetry laisse la politique de réessai temporiser ; utilisez
// setsPermanentFailure pour un 404 ou une URL mauvaise
Result := setsRetry;
end;
end;
end;
// Mise à niveau B-B vers B-LT en un appel pour chaque signature du fichier chargé
var
Pdf: THotPDF;
Upgraded: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.BeginIncrementalUpdate('signed.pdf');
Upgraded := Pdf.PopulatePAdESLTVEvidence(FetchEvidence,
THPDFSignatureEvidenceRetryPolicy.Default);
if Upgraded > 0 then
// Sauvegarde en ajout seul : les octets que les signatures existantes
// couvrent sont préservés verbatim
Pdf.SaveIncrementalUpdate('signed-lt.pdf');
finally
Pdf.Free;
end;
end;
Les échecs sont par signature, pas par document. Un répondeur qui dépasse le délai pour un signataire saute le matériel de ce signataire et laisse le reste de la passe intact, ce qui est le comportement voulu dans un lot : des preuves partielles valent mieux qu'une exécution abandonnée, et la valeur de retour vous dit combien de signatures se sont réellement améliorées
La chaîne que le CMS a oublié d'inclure
La vérification de révocation a besoin du certificat de l'émetteur, et un nombre surprenant de piles de signature omettent les intermédiaires du conteneur CMS. La voie de récupération est l'extension Authority Information Access, méthode d'accès 1.3.6.1.5.5.7.48.2, qui annonce une URL où le certificat de l'émetteur peut être téléchargé. HPDFFetchAIAIntermediates parcourt ces URLs via le même transport, extrait le DER de chaque réponse, et ne renvoie que les certificats que le CMS ne portait pas déjà, indexés par hachage DER pour que doublons et boucles ne puissent tourner
Deux détails décident si cela fonctionne contre de vraies autorités de certification. Le premier est l'encodage : les points de terminaison des AC servent le certificat en DER nu aussi souvent qu'en blindage PEM, et il n'y a aucun type de contenu fiable pour les distinguer. La sonde robuste est textuelle, puis structurelle. Cherchez le marqueur -----BEGIN CERTIFICATE-----, ôtez le blindage et décodez le base64 s'il est présent, et dans les deux voies confirmez que le premier octet du résultat est $30, la balise DER d'une SEQUENCE. Le second est la profondeur : un intermédiaire récupéré peut lui-même annoncer une URL AIA pour son propre émetteur, donc le parcours ajoute de nouveaux candidats à la file et complète des chaînes courtes de deux ou trois sauts. Il faut le plafonner, et c'est à cela que sert le paramètre MaxFetch
Qu'est-ce qu'une seed value de signature, et pourquoi échoue-t-elle en silence ?
Une seed value est une contrainte que l'auteur du document attache à un champ de signature pour dire au signataire quel type de signature est acceptable : quel SubFilter, quel algorithme de condensé, quelles raisons, quelle version minimale de PDF, si les informations de révocation doivent être intégrées. Elle vit dans un dictionnaire /SV sur le champ et est définie dans l'ISO 32000-1 §12.7.5.5. HotPDF l'écrit avec AttachPAdESSeedValue et la vérifie avec CheckLoadedSignatureSeedValue, qui renvoie True quand le champ est sans contrainte ou que chaque contrainte présente passe, et sur False nomme la première contrainte en échec via un paramètre de sortie que vous pouvez mettre directement dans un message d'erreur
Le mécanisme qui rend les seed values faciles à rater est l'entrée de drapeaux /Ff décrite en §12.7.5.5.3. Un bit activé marque sa contrainte comme exigée : une divergence est une erreur et le signataire doit refuser. Un bit désactivé marque la même contrainte comme préférence : la valeur filtre ce que l'IU devrait offrir et rien de plus. Deux pièges suivent de là. D'abord, /Ff vit à l'intérieur du dictionnaire /SV, pas sur l'annotation widget, donc le code qui lit le /Ff au niveau du champ obtient une réponse vide pour toujours et conclut que rien n'est forcé. Ensuite, les affectations de bits ne sont pas une simple suite de un, deux, quatre, huit ; dans HotPDF l'écrivain émet 2 pour SubFilter, 4 pour MinVersion, 32 pour AddRevInfo et 64 pour DigestMethod. Un lecteur qui suppose des bits séquentiels décode chaque contrainte comme optionnelle et passe chaque test sauf celui qui compte
var
Violation: AnsiString;
begin
// Demander au champ si le profil avec lequel nous allons signer est permis
if not Pdf.CheckLoadedSignatureSeedValue(0, 'ETSI.CAdES.detached',
'SHA256', 'Approved for payment', 1, Violation) then
raise Exception.Create('Signature field rejects this profile: ' +
String(Violation));
// Contrainte satisfaite : poursuivre avec la passe de signature
end;
Le test qui a exposé le bug de décodage d'origine n'était pas un test positif. C'était l'assertion qu'une divergence forcée doit être rejetée, et c'est le seul type de test capable d'attraper cette classe de défaut : un décodeur qui lit le mauvais dictionnaire ou les mauvaises positions de bits produit « aucune contrainte violée » pour chaque entrée, ce qui ressemble exactement à un comportement correct jusqu'à ce que vous en violiez une délibérément
Où cela se situe sur l'échelle LTV
Quatre barreaux, et chacun a besoin de celui du dessous. B-B est la signature nue. B-T ajoute un horodatage de confiance, qui fixe l'heure de signature pour qu'un validateur sache quel moment évaluer pour la révocation. B-LT ajoute les preuves de révocation au DSS, et c'est ce que PopulatePAdESLTVEvidence automatise. B-LTA ajoute des horodatages de document renouvelés avant que le précédent ne faiblisse, étendant la validité indéfiniment ; HotPDF expose cela comme RenewPAdESLTATimestamp, qui ajoute un nouvel horodatage comme révision incrémentale et préserve chaque signature, horodatage et entrée DSS antérieurs intacts
Un modèle de mise à jour incrémentale est la seule façon correcte d'ajouter des preuves à un document signé, car réécrire le fichier casserait les plages d'octets que les signatures existantes couvrent. Si vous devez raisonner sur ce qui a changé entre révisions, et si ces changements sont du genre qu'une signature permet, cette analyse est couverte séparément dans l'analyse de révisions DocMDP et FieldMDP. Le pipeline de signature lui-même, y compris les sources de certificats et les pièges d'ordre des octets, se trouve dans le guide de signature PAdES, et le côté validation est dans la vérification des signatures sur documents chargés
Un avertissement pratique sur l'ordre. Collectez les preuves le plus tôt possible après la signature, idéalement dans le même travail. Les répondeurs capables de répondre pour un certificat sont en ligne tant que le certificat est courant et partis des années plus tard, donc un document qui quitte votre pipeline en B-B pourrait ne plus jamais être actualisable. HotPDF fonctionne comme un composant VCL natif pour Delphi et C++Builder, et toute la passe de preuves est en processus hormis votre propre transport ; les profils pris en charge sont listés sur la page produit du HotPDF Delphi PDF component