Tutorial: PAdES PDF ondertekenen in Delphi

Een complete doorloop die een ongetekende factuur-PDF neemt en een PAdES-B-T-document produceert met een ingebedde RFC 3161-timestamp — het niveau dat wordt verwacht door eIDAS, AdES-verifiers en de meeste publieke portals. Je configureert een key-provider (PKCS#11-smartcard of Windows-certificaatopslag), kiest het juiste PAdES-profiel en verifieert het ondertekende bestand end-to-end.

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

Wat je gaat bouwen

Een VCL Forms-applicatie die een PDF laadt, deze ondertekent met een smartcard of PFX, een vertrouwde timestamp inbedt en een verifieerbaar PAdES-bestand naar disk schrijft.

Kies een PAdES-niveau

PAdES-B-B (basis), PAdES-B-T (met timestamp), PAdES-B-LT (lange termijn met revocation-data) of PAdES-B-LTA (lange termijn met archief-timestamp). PAdES-B-T is de zinvolle standaard voor facturen, contracten en de meeste publieke workflows.

Laad een sleutel

sgcSign ondersteunt 10 key-providers. Deze tutorial behandelt TsgcPKCS11KeyProvider voor hardware-tokens en TsgcWindowsStoreKeyProvider voor certificaten die al in Windows zijn geinstalleerd. Hetzelfde code-pad werkt voor PFX, PEM, Azure Trusted Signing, AWS KMS en Google KMS.

Onderteken en verifieer

TsgcSignPDF stuurt de signing-pipeline aan, TsgcSignProfile_PAdES configureert het profiel, en TsgcSignatureVerifier valideert het uiteindelijke document. Elke stap stelt events bloot zodat je kunt inspecteren wat er in elke fase gebeurt.

Voeg de units toe

Elke key-provider zit in zijn eigen unit. Refereer alleen aan de provider die je nodig hebt, zodat je geen units linkt die je niet gebruikt.

Delphi uses-clausule

Voor deze tutorial halen we twee key-providers binnen (PKCS#11 en Windows-store) en de PAdES-signing-units. sgcSign_TSA is de RFC 3161-timestamp-client — het PAdES-profiel gebruikt deze automatisch wanneer je een TSA-URL instelt.

Als je alleen een PFX-bestand hebt, vervang sgcSign_KeyProvider_PKCS11 door sgcSign_KeyProvider_PFX en gebruik TsgcPFXKeyProvider zoals getoond in de 5-minuten-snelstart.

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

Configureer een key-provider

Twee gangbare keuzes — een PKCS#11-hardware-token (smartcards, USB-HSM’s) of een certificaat dat al in de Windows-certificaatopslag staat.

PKCS#11-smartcard

Wijs ModulePath naar de PKCS#11-DLL van de leverancier (bijvoorbeeld C:\Windows\System32\eTPKCS11.dll voor SafeNet). De Slot-property selecteert het token-slot — gebruik 0 voor het eerste beschikbare token. De PIN wordt geleverd via het OnPinPrompt-event zodat hij nooit in de source wordt opgeslagen.

Het aanroepen van LoadFromToken opent een sessie, vindt het signing-certificaat op label of thumbprint en bereidt het private-key-handle voor. Geen bytes verlaten het token — het feitelijke RSA-PSS- of ECDSA-ondertekenen gebeurt op het apparaat.

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;

Windows-certificaatopslag

Als je certificaat al in de Windows-store is geinstalleerd — bijvoorbeeld omdat je een qualified eID-kaart met een vendor-mini-driver gebruikt — gebruik dan TsgcWindowsStoreKeyProvider. De provider praat met CNG, dus SHA-256/SHA-384 werken ongeacht de oorspronkelijke CSP.

Match op subject, op thumbprint of op serienummer. StoreLocation op slCurrentUser zetten gebruikt de personal store; slLocalMachine gebruikt de machine store en vereist meestal administrator-rechten.

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

Configureer het PAdES-B-T-profiel

PAdES-B-T voegt een RFC 3161-timestamp toe aan de handtekening zodat verifiers kunnen bewijzen dat het document voor een bekend tijdstip is ondertekend — ook als het signing-certificaat later verloopt of wordt ingetrokken.

Profiel-properties

Level selecteert het AdES-niveau — palB, palT, palLT of palLTA. Voor PAdES-B-T zet je het op palT; het profiel verwacht dan een niet-lege TSA.URL.

HashAlgorithm staat standaard op shaSHA256, wat elke moderne verifier verwacht. SHA-384 en SHA-512 zijn ook beschikbaar; SHA-1 wordt opzettelijk niet aangeboden omdat eIDAS dit niet langer accepteert.

Het SignatureField-sub-object regelt waar de zichtbare signature-widget binnen de PDF wordt geplaatst — paginanummer, rechthoek-coordinaten en optionele reason- / location- / contact-tekstvelden. Laat het leeg voor onzichtbare handtekeningen (de standaard).

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;

Onderteken de PDF

Bedraad de key-provider en het profiel in TsgcSignPDF, en roep dan SignFile aan met de input- en output-bestandsnamen.

Volledige sign-aanroep

SignFile leest de input-PDF, berekent het document-digest, roept de key-provider aan om een handtekening te produceren, haalt een timestamp op van de TSA en schrijft het resultaat. Als de input al handtekeningen bevat, voegt sgcSign een incrementele update toe — oorspronkelijke handtekeningen blijven geldig.

Het OnProgress-event rapporteert elke fase (reading, hashing, signing, timestamping, writing), nuttig voor voortgangsbalken op meerdere-MB-bestanden.

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;

Verifieer de ondertekende PDF

Een ondertekend document is alleen nuttig als het verifieert. TsgcSignatureVerifier valideert de cryptografische handtekening, de certificate chain en de ingebedde timestamp in een aanroep.

Het resultaat lezen

De VerifyFile-methode geeft een TsgcSignatureReport terug met een uitsplitsing per handtekening. Voor elke entry krijg je Status (svValid, svInvalid, svUnknown), de signer-subject, het feitelijk gedetecteerde AdES-niveau, de timestamp-waarde en eventuele chain- of revocation-problemen.

Vergelijk Report.Level met je verwachte niveau — je wilde palT, dus elke verificatie die palB rapporteert, betekent dat het timestamp-verzoek stilletjes faalde. De TSA-URL is de gebruikelijke verdachte.

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;

Wat meestal misgaat

Een korte lijst van problemen die we het vaakst zien wanneer ontwikkelaars hun eerste PAdES-document ondertekenen.

TSA geeft HTTP 200 maar een misvormde response terug

Sommige TSA’s zijn alleen HTTPS en weigeren plain http://-URL’s met een zachte fout. Als Report.Level terugkomt als palB wanneer je om palT vroeg, schakel de TSA-URL over op de https://-tegenhanger. De meeste publieke TSA’s publiceren beide schema’s.

Certificaat heeft geen extended key usage

Testcertificaten missen vaak de id-kp-documentSigning-EKU. Adobe Reader toont een waarschuwing ook al is de cryptografische handtekening geldig. Vraag voor productie een certificaat aan met expliciete Document Signing-EKU bij je CA.

PDF/A-documenten hebben PAdES-B-LT nodig

PDF/A-2- en PDF/A-3-archieven vereisen dat alle revocation-informatie in het document zelf is ingebed. Gebruik palLT of palLTA — sgcSign haalt OCSP-responses en CRL’s op tijdens ondertekenen en bedt ze in in het DSS-dictionary.

Smartcard-sessies verlopen

Sommige PKCS#11-drivers sluiten de sessie na enkele seconden inactiviteit. Houd de TsgcPKCS11KeyProvider-instantie levend gedurende de hele signing-operatie en roep Free pas aan nadat SignFile terugkeert.

Waar nu verder

Langetermijn-archivering, landenprofielen, of verplaats het ondertekenen van de desktop naar een gecentraliseerde server.

21 landenprofielen

One-liners voor VeriFactu, FatturaPA, KSeF, FACTUR-X en de EU-werkcontract-profielen — allemaal gebouwd bovenop dezelfde PAdES- / XAdES-engine.

Lees meer →

sgcSign Server

Verplaats het ondertekenen naar een self-hosted daemon — REST-API, GitHub Actions-integratie, Authenticode en ClickOnce bovenop PAdES.

Lees meer →

10 key-providers

Voorbij PFX en Windows-store — Azure Trusted Signing, AWS KMS, Google KMS, HashiCorp Vault, Certum en CSC v2 cloud-providers.

Lees meer →

Klaar om je eerste PDF te ondertekenen?

Download de proefversie, draai deze tutorial tegen je eigen factuur, licentieer sgcSign wanneer je live gaat.