Tutorial: firma PDF PAdES in Delphi

Una guida completa che prende una fattura PDF non firmata e produce un documento PAdES-B-T con un timestamp RFC 3161 integrato — il livello richiesto da eIDAS, dai verificatori AdES e dalla maggior parte dei portali della pubblica amministrazione. Configurerai un key provider (smart card PKCS#11 o Windows certificate store), sceglierai il profilo PAdES corretto e verificherai il file firmato end-to-end.

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

Cosa costruirai

Un'applicazione VCL Forms che carica un PDF, lo firma con una smart card o un PFX, integra un timestamp affidabile e scrive su disco un file PAdES verificabile.

Scegli un livello PAdES

PAdES-B-B (base), PAdES-B-T (con timestamp), PAdES-B-LT (lungo termine con dati di revoca) o PAdES-B-LTA (lungo termine con timestamp d'archivio). PAdES-B-T è la scelta predefinita sensata per fatture, contratti e la maggior parte dei flussi della pubblica amministrazione.

Carica una chiave

sgcSign supporta 10 key provider. Questo tutorial copre TsgcPKCS11KeyProvider per i token hardware e TsgcWindowsStoreKeyProvider per i certificati già installati in Windows. Lo stesso flusso di codice funziona per PFX, PEM, Azure Trusted Signing, AWS KMS e Google KMS.

Firma e verifica

TsgcSignPDF guida la pipeline di firma, TsgcSignProfile_PAdES configura il profilo e TsgcSignatureVerifier convalida il documento finale. Ogni passo espone eventi per ispezionare ciò che accade in ogni fase.

Aggiungi le unit

Ogni key provider vive nella propria unit. Referenzia solo il provider che ti serve, per non collegare unit che non userai.

Clausola uses di Delphi

Per questo tutorial includiamo due key provider (PKCS#11 e Windows Store) e le unit di firma PAdES. sgcSign_TSA è il client di timestamp RFC 3161 — il profilo PAdES lo usa automaticamente quando imposti un URL della TSA.

Se hai solo un file PFX, sostituisci sgcSign_KeyProvider_PKCS11 con sgcSign_KeyProvider_PFX e usa TsgcPFXKeyProvider come mostrato nella Guida Rapida in 5 minuti.

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

Configura un key provider

Due scelte comuni — un token hardware PKCS#11 (smart card, HSM USB) oppure un certificato già presente nel Windows certificate store.

Smart card PKCS#11

Punta ModulePath alla DLL PKCS#11 del produttore (per esempio C:\Windows\System32\eTPKCS11.dll per SafeNet). La proprietà Slot seleziona lo slot del token — usa 0 per il primo token disponibile. Il PIN viene fornito tramite l'evento OnPinPrompt, così non viene mai memorizzato nel codice sorgente.

Chiamare LoadFromToken apre una sessione, individua il certificato di firma per etichetta o thumbprint e prepara l'handle della chiave privata. Nessun byte esce dal token — la firma RSA-PSS o ECDSA vera e propria avviene sul dispositivo.

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 certificate store

Se il tuo certificato è già installato nel Windows store — per esempio perché usi una carta eID qualificata con un mini-driver del produttore — usa invece TsgcWindowsStoreKeyProvider. Il provider dialoga con CNG, quindi SHA-256/SHA-384 funzionano indipendentemente dal CSP originale.

Filtra per subject, per thumbprint o per numero seriale. Impostare StoreLocation su slCurrentUser usa lo store personale; slLocalMachine usa quello della macchina e di norma richiede diritti di amministratore.

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

Configura il profilo PAdES-B-T

PAdES-B-T aggiunge un timestamp RFC 3161 alla firma, così i verificatori possono dimostrare che il documento è stato firmato prima di un punto noto nel tempo — anche se il certificato di firma in seguito scade o viene revocato.

Proprietà del profilo

Level seleziona il livello AdES — palB, palT, palLT o palLTA. Per PAdES-B-T impostalo su palT; il profilo si aspetta poi un TSA.URL non vuoto.

HashAlgorithm ha come default shaSHA256, che è quanto si aspettano tutti i verificatori moderni. Sono disponibili anche SHA-384 e SHA-512; SHA-1 non è intenzionalmente offerto perché eIDAS non lo accetta più.

Il sotto-oggetto SignatureField controlla dove viene posizionato il widget di firma visibile all'interno del PDF — numero di pagina, coordinate del rettangolo e campi di testo opzionali per motivo / luogo / contatto. Lascialo vuoto per firme invisibili (il default).

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;

Firma il PDF

Collega il key provider e il profilo a TsgcSignPDF, poi chiama SignFile con i nomi dei file di input e output.

Chiamata di firma completa

SignFile legge il PDF di input, calcola il digest del documento, chiama il key provider per produrre una firma, recupera un timestamp dalla TSA e scrive il risultato. Se l'input contiene già firme, sgcSign accoda un aggiornamento incrementale — le firme originali restano valide.

L'evento OnProgress riporta ogni fase (lettura, hash, firma, timestamping, scrittura), utile per le progress bar su file di vari MB.

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;

Verifica il PDF firmato

Un documento firmato è utile solo se si verifica. TsgcSignatureVerifier convalida la firma crittografica, la catena dei certificati e il timestamp integrato in una sola chiamata.

Leggere il risultato

Il metodo VerifyFile restituisce un TsgcSignatureReport con un'analisi per ogni firma. Per ciascuna voce ottieni Status (svValid, svInvalid, svUnknown), il subject del firmatario, il livello AdES effettivamente rilevato, il valore del timestamp ed eventuali problemi di catena o revoca.

Confronta Report.Level con il livello atteso — volevi palT, quindi qualunque verifica che riporti palB significa che la richiesta del timestamp è fallita silenziosamente. L'URL della TSA è il sospettato abituale.

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;

Cosa va storto di solito

Un breve elenco dei problemi che vediamo più spesso quando gli sviluppatori firmano il loro primo documento PAdES.

La TSA restituisce HTTP 200 ma una risposta malformata

Alcune TSA sono solo HTTPS e rifiutano gli URL http:// con un soft error. Se Report.Level torna come palB mentre tu avevi chiesto palT, passa l'URL della TSA al corrispondente https://. La maggior parte delle TSA pubbliche pubblica entrambi gli schemi.

Il certificato non ha l'extended key usage

I certificati di test spesso mancano dell'EKU id-kp-documentSigning. Adobe Reader mostrerà un avviso anche se la firma crittografica è valida. In produzione, richiedi alla tua CA un certificato con l'EKU Document Signing esplicito.

I documenti PDF/A richiedono PAdES-B-LT

Gli archivi PDF/A-2 e PDF/A-3 richiedono che tutte le informazioni di revoca siano integrate nel documento stesso. Usa palLT o palLTA — sgcSign raccoglierà le risposte OCSP e i CRL al momento della firma e li integrerà nel dizionario DSS.

Le sessioni delle smart card vanno in timeout

Alcuni driver PKCS#11 chiudono la sessione dopo pochi secondi di inattività. Tieni in vita l'istanza di TsgcPKCS11KeyProvider per l'intera operazione di firma e chiama Free solo dopo che SignFile è tornato.

Dove andare da qui

Archiviazione a lungo termine, profili nazionali, oppure sposta la firma fuori dal desktop in un server centralizzato.

21 profili nazionali

Configurazioni one-liner per VeriFactu, FatturaPA, KSeF, FACTUR-X e i profili UE per i contratti di lavoro — tutti costruiti sopra lo stesso engine PAdES / XAdES.

Leggi di più →

sgcSign Server

Sposta la firma in un demone self-hosted — API REST, integrazione GitHub Actions, Authenticode e ClickOnce sopra PAdES.

Leggi di più →

10 key provider

Oltre a PFX e Windows store — Azure Trusted Signing, AWS KMS, Google KMS, HashiCorp Vault, Certum e i provider cloud CSC v2.

Leggi di più →

Pronto a firmare il tuo primo PDF?

Scarica la trial, esegui questo tutorial sulla tua fattura, e quando spedisci acquista la licenza di sgcSign.