Article technique

Preuves LTV PAdES et seed values dans HotPDF

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

Flux PopulatePAdESLTVEvidence de HotPDF : le transport FetchEvidence fourni par l'appelant, les champs de l'enregistrement de requête et les statuts par signature
Chaque octet réseau passe par votre callback FetchEvidence, et chaque signature obtient son propre statut pour qu'un dépassement de délai n'interrompe jamais la passe
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

Diagramme de complétion de chaîne AIA pour HotPDF : récupération d'URL caIssuers, sonde PEM contre DER, déduplication par hachage DER et plafond de profondeur MaxFetch
HPDFFetchAIAIntermediates parcourt les URLs caIssuers via le même transport, sondant le blindage PEM et plafonnant la file avec 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

Table des bits de drapeaux de seed value pour la signature PAdES HotPDF montrant les bits Ff 2, 4, 32 et 64 et le traitement de contrainte exigée contre préférée
L'entrée /Ff vit à l'intérieur de /SV, et chaque position de bit décide si une divergence est un refus ferme ou une préférence d'interface
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