Tutoriel : signature PDF PAdES en Delphi

Une marche à suivre complète qui prend une facture PDF non signée et produit un document PAdES-B-T avec un horodatage RFC 3161 embarqué — le niveau attendu par eIDAS, les vérificateurs AdES et la plupart des portails du secteur public. Vous configurerez un fournisseur de clés (carte à puce PKCS#11 ou magasin de certificats Windows), choisirez le bon profil PAdES et vérifierez le fichier signé de bout en bout.

PAdES-B-B / B-T / B-LT / B-LTA
PKCS#11 / Windows Store / PFX
Delphi 7 – RAD Studio 13

Ce que vous allez construire

Une application VCL Forms qui charge un PDF, le signe avec une carte à puce ou un PFX, embarque un horodatage de confiance et écrit un fichier PAdES vérifiable sur disque.

Choisir un niveau PAdES

PAdES-B-B (basique), PAdES-B-T (avec horodatage), PAdES-B-LT (long terme avec données de révocation) ou PAdES-B-LTA (long terme avec horodatage d'archive). PAdES-B-T est le défaut sensé pour les factures, contrats et la plupart des workflows du secteur public.

Charger une clé

sgcSign supporte 10 fournisseurs de clés. Ce tutoriel couvre TsgcPKCS11KeyProvider pour les tokens matériels et TsgcWindowsStoreKeyProvider pour les certificats déjà installés sous Windows. Le même chemin de code fonctionne pour PFX, PEM, Azure Trusted Signing, AWS KMS et Google KMS.

Signer et vérifier

TsgcSignPDF pilote le pipeline de signature, TsgcSignProfile_PAdES configure le profil, et TsgcSignatureVerifier valide le document final. Chaque étape expose des événements pour que vous puissiez inspecter ce qui se passe à chaque stade.

Ajouter les unités

Chaque fournisseur de clés vit dans sa propre unité. Ne référencez que le fournisseur dont vous avez besoin afin de ne pas lier des unités que vous n'utiliserez pas.

Clause uses Delphi

Pour ce tutoriel nous incluons deux fournisseurs de clés (PKCS#11 et Windows Store) et les unités de signature PAdES. sgcSign_TSA est le client d'horodatage RFC 3161 — le profil PAdES l'utilise automatiquement quand vous définissez une URL TSA.

Si vous n'avez qu'un fichier PFX, remplacez sgcSign_KeyProvider_PKCS11 par sgcSign_KeyProvider_PFX et utilisez TsgcPFXKeyProvider comme montré dans le Démarrage rapide en 5 minutes.

uPDFSigning.pas
uses
  Classes, SysUtils,
  // sgc
  sgcSign_KeyProvider_PKCS11,
  sgcSign_KeyProvider_WindowsStore,
  sgcSign_PDF,
  sgcSign_Profile_PAdES,
  sgcSign_TSA,
  sgcSign_Verifier;

Configurer un fournisseur de clés

Deux choix courants — un token matériel PKCS#11 (cartes à puce, HSM USB) ou un certificat déjà dans le magasin de certificats Windows.

Carte à puce PKCS#11

Pointez ModulePath vers la DLL PKCS#11 du fournisseur (par exemple C:\Windows\System32\eTPKCS11.dll pour SafeNet). La propriété Slot sélectionne le slot du token — utilisez 0 pour le premier token disponible. Le PIN est livré via l'événement OnPinPrompt et n'est donc jamais stocké dans les sources.

Appeler LoadFromToken ouvre une session, trouve le certificat de signature par label ou empreinte et prépare le handle de clé privée. Aucun octet ne quitte le token — la signature RSA-PSS ou ECDSA effective a lieu sur l'appareil.

step1-pkcs11.pas
var
  vKeyProvider: TsgcPKCS11KeyProvider;
begin
  vKeyProvider := TsgcPKCS11KeyProvider.Create(nil);
  try
    vKeyProvider.ModulePath := 'C:\Windows\System32\eTPKCS11.dll';
    vKeyProvider.Slot := 0;
    vKeyProvider.CertificateLabel := 'My Signing Cert';
    vKeyProvider.OnPinPrompt :=
      procedure(Sender: TObject; var aPIN: string)
      begin
        aPIN := InputBox('Smart card', 'PIN:', '');
      end;
    vKeyProvider.LoadFromToken;
    // vKeyProvider is now ready to sign
  finally
    // keep alive until signing finishes, then Free
  end;
end;

Magasin de certificats Windows

Si votre certificat est déjà installé dans le magasin Windows — par exemple parce que vous utilisez une carte eID qualifiée avec un mini-driver fournisseur — utilisez TsgcWindowsStoreKeyProvider à la place. Le fournisseur parle à CNG, donc SHA-256/SHA-384 fonctionnent quel que soit le CSP d'origine.

Correspondance par sujet, par empreinte ou par numéro de série. Définir StoreLocation à slCurrentUser utilise le magasin personnel ; slLocalMachine utilise le magasin machine et nécessite typiquement des droits administrateur.

step1-winstore.pas
var
  vKeyProvider: TsgcWindowsStoreKeyProvider;
begin
  vKeyProvider := TsgcWindowsStoreKeyProvider.Create(nil);
  vKeyProvider.StoreLocation := slCurrentUser;
  vKeyProvider.StoreName := 'MY';
  vKeyProvider.Thumbprint :=
    '1234ABCD5678EF901234567890ABCDEF12345678';
  vKeyProvider.LoadFromStore;
end;

Configurer le profil PAdES-B-T

PAdES-B-T ajoute un horodatage RFC 3161 à la signature pour que les vérificateurs puissent prouver que le document a été signé avant un point connu dans le temps — même si le certificat de signature expire ou est révoqué plus tard.

Propriétés du profil

Level sélectionne le niveau AdES — palB, palT, palLT ou palLTA. Pour PAdES-B-T définissez-le à palT ; le profil s'attend alors à un TSA.URL non vide.

HashAlgorithm par défaut est shaSHA256, ce que tout vérificateur moderne attend. SHA-384 et SHA-512 sont aussi disponibles ; SHA-1 n'est intentionnellement pas proposé car eIDAS ne l'accepte plus.

Le sous-objet SignatureField contrôle où le widget de signature visible est placé dans le PDF — numéro de page, coordonnées du rectangle et champs de texte optionnels raison / lieu / contact. Laissez-le vide pour des signatures invisibles (le défaut).

step2-profile.pas
var
  vProfile: TsgcSignProfile_PAdES;
begin
  vProfile := TsgcSignProfile_PAdES.Create(nil);
  vProfile.Level := palT;
  vProfile.HashAlgorithm := shaSHA256;

  // RFC 3161 timestamp authority
  vProfile.TSA.URL := 'http://timestamp.digicert.com';
  vProfile.TSA.HashAlgorithm := shaSHA256;

  // Optional visible signature widget
  vProfile.SignatureField.Visible := True;
  vProfile.SignatureField.Page := 1;
  vProfile.SignatureField.Rect := Rect(50, 50, 250, 120);
  vProfile.SignatureField.Reason := 'Approved';
  vProfile.SignatureField.Location := 'Madrid';
end;

Signer le PDF

Câblez le fournisseur de clés et le profil dans TsgcSignPDF, puis appelez SignFile avec les noms de fichier d'entrée et de sortie.

Appel de signature complet

SignFile lit le PDF d'entrée, calcule le digest du document, appelle le fournisseur de clés pour produire une signature, récupère un horodatage depuis la TSA et écrit le résultat. Si l'entrée contient déjà des signatures, sgcSign ajoute une mise à jour incrémentale — les signatures originales restent valides.

L'événement OnProgress rapporte chaque phase (lecture, hachage, signature, horodatage, écriture), utile pour les barres de progression sur des fichiers de plusieurs Mo.

step3-sign.pas
var
  vSigner: TsgcSignPDF;
begin
  vSigner := TsgcSignPDF.Create(nil);
  try
    vSigner.KeyProvider := vKeyProvider;
    vSigner.Profile := vProfile;
    vSigner.OnProgress :=
      procedure(Sender: TObject; const aPhase: string; aPct: Integer)
      begin
        Memo1.Lines.Add(Format('%s — %d%%', [aPhase, aPct]));
      end;
    vSigner.SignFile('invoice.pdf', 'invoice-signed.pdf');
  finally
    vSigner.Free;
  end;
end;

Vérifier le PDF signé

Un document signé n'est utile que s'il se vérifie. TsgcSignatureVerifier valide la signature cryptographique, la chaîne de certificats et l'horodatage embarqué en un seul appel.

Lire le résultat

La méthode VerifyFile retourne un TsgcSignatureReport avec un détail par signature. Pour chaque entrée vous obtenez Status (svValid, svInvalid, svUnknown), le sujet du signataire, le niveau AdES réellement détecté, la valeur d'horodatage, et tout problème de chaîne ou de révocation.

Comparez Report.Level au niveau attendu — vous vouliez palT, donc toute vérification qui rapporte palB signifie que la requête d'horodatage a échoué silencieusement. L'URL TSA est le suspect habituel.

step4-verify.pas
var
  vVerifier: TsgcSignatureVerifier;
  vReport: TsgcSignatureReport;
  i: Integer;
begin
  vVerifier := TsgcSignatureVerifier.Create(nil);
  try
    vReport := vVerifier.VerifyFile('invoice-signed.pdf');
    for i := 0 to vReport.SignatureCount - 1 do
    begin
      Memo1.Lines.Add('Signer:    ' + vReport.Signatures[i].Subject);
      Memo1.Lines.Add('Level:     ' + vReport.Signatures[i].LevelAsString);
      Memo1.Lines.Add('Status:    ' + vReport.Signatures[i].StatusAsString);
      Memo1.Lines.Add('TSA time:  ' +
        DateTimeToStr(vReport.Signatures[i].TimestampUTC));
    end;
  finally
    vVerifier.Free;
  end;
end;

Ce qui se passe mal habituellement

Une courte liste des problèmes que nous voyons le plus souvent quand des développeurs signent leur premier document PAdES.

La TSA retourne HTTP 200 mais une réponse malformée

Certaines TSA sont uniquement HTTPS et rejettent les URL http:// en clair avec une erreur silencieuse. Si Report.Level revient à palB alors que vous demandiez palT, basculez l'URL TSA vers son équivalent https://. La plupart des TSA publiques publient les deux schémas.

Le certificat n'a pas d'extended key usage

Les certificats de test manquent souvent de l'EKU id-kp-documentSigning. Adobe Reader affichera un avertissement même si la signature cryptographique est valide. Pour la production, demandez un certificat avec EKU Document Signing explicite à votre CA.

Les documents PDF/A nécessitent PAdES-B-LT

Les archives PDF/A-2 et PDF/A-3 nécessitent que toutes les informations de révocation soient embarquées dans le document lui-même. Utilisez palLT ou palLTA — sgcSign récoltera les réponses OCSP et CRL au moment de la signature et les embarquera dans le dictionnaire DSS.

Les sessions de cartes à puce expirent

Certains drivers PKCS#11 ferment la session après quelques secondes d'inactivité. Gardez l'instance TsgcPKCS11KeyProvider vivante pendant toute l'opération de signature et n'appelez Free qu'après le retour de SignFile.

Où aller à partir d'ici

Archivage long terme, profils nationaux, ou déplacement de la signature hors du bureau vers un serveur centralisé.

21 profils nationaux

Lignes uniques pour VeriFactu, FatturaPA, KSeF, FACTUR-X et les profils contrats de travail UE — tous construits sur le même moteur PAdES / XAdES.

En savoir plus →

Serveur sgcSign

Déplacez la signature dans un daemon auto-hébergé — API REST, intégration GitHub Actions, Authenticode et ClickOnce par-dessus PAdES.

En savoir plus →

10 fournisseurs de clés

Au-delà du PFX et du magasin Windows — Azure Trusted Signing, AWS KMS, Google KMS, HashiCorp Vault, Certum et les fournisseurs cloud CSC v2.

En savoir plus →

Prêt à signer votre premier PDF ?

Téléchargez l'essai, exécutez ce tutoriel contre votre propre facture, licenciez sgcSign quand vous livrez.