Tutorial: PAdES-PDF-Signierung in Delphi

Ein kompletter Durchgang, der aus einer unsignierten Rechnungs-PDF ein PAdES-B-T-Dokument mit eingebettetem RFC-3161-Zeitstempel macht — das Niveau, das eIDAS, AdES-Validatoren und die meisten Behördenportale erwarten. Du konfigurierst einen Key Provider (PKCS#11-Smartcard oder Windows-Zertifikatsspeicher), wählst das passende PAdES-Profil und verifizierst die signierte Datei Ende-zu-Ende.

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

Was du bauen wirst

Eine VCL-Forms-Anwendung, die ein PDF lädt, es mit einer Smartcard oder PFX-Datei signiert, einen vertrauenswürdigen Zeitstempel einbettet und eine verifizierbare PAdES-Datei auf die Festplatte schreibt.

Ein PAdES-Level wählen

PAdES-B-B (Basic), PAdES-B-T (mit Zeitstempel), PAdES-B-LT (Long-Term mit Sperrinformationen) oder PAdES-B-LTA (Long-Term mit Archivzeitstempel). PAdES-B-T ist der sinnvolle Standard für Rechnungen, Verträge und die meisten Behörden-Workflows.

Einen Schlüssel laden

sgcSign unterstützt 10 Key Provider. Dieses Tutorial behandelt TsgcPKCS11KeyProvider für Hardware-Token und TsgcWindowsStoreKeyProvider für bereits in Windows installierte Zertifikate. Derselbe Codepfad funktioniert für PFX, PEM, Azure Trusted Signing, AWS KMS und Google KMS.

Signieren und verifizieren

TsgcSignPDF steuert die Signatur-Pipeline, TsgcSignProfile_PAdES konfiguriert das Profil und TsgcSignatureVerifier validiert das fertige Dokument. Jeder Schritt liefert Events, sodass du jede Phase einsehen kannst.

Units einbinden

Jeder Key Provider hat seine eigene Unit. Binde nur die ein, die du wirklich brauchst, damit du keine ungenutzten Units linkst.

Delphi-uses-Klausel

Für dieses Tutorial holen wir zwei Key Provider (PKCS#11 und Windows Store) sowie die PAdES-Signatur-Units herein. sgcSign_TSA ist der RFC-3161-Zeitstempel-Client — das PAdES-Profil verwendet ihn automatisch, sobald du eine TSA-URL setzt.

Wenn du nur eine PFX-Datei hast, ersetze sgcSign_KeyProvider_PKCS11 durch sgcSign_KeyProvider_PFX und verwende TsgcPFXKeyProvider wie im 5-Minuten-Schnellstart gezeigt.

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

Einen Key Provider konfigurieren

Zwei verbreitete Varianten — ein PKCS#11-Hardware-Token (Smartcards, USB-HSMs) oder ein Zertifikat, das bereits im Windows-Zertifikatsspeicher liegt.

PKCS#11-Smartcard

Setze ModulePath auf die PKCS#11-DLL des Herstellers (z. B. C:\Windows\System32\eTPKCS11.dll für SafeNet). Die Eigenschaft Slot wählt den Token-Slot — verwende 0 für das erste verfügbare Token. Die PIN wird über das Event OnPinPrompt übergeben, sodass sie nie im Quellcode steht.

Der Aufruf von LoadFromToken öffnet eine Session, sucht das Signaturzertifikat über Label oder Thumbprint und bereitet das Handle des privaten Schlüssels vor. Es verlässt kein einziges Byte das Token — die eigentliche RSA-PSS- oder ECDSA-Signatur entsteht auf dem Gerät.

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-Zertifikatsspeicher

Wenn dein Zertifikat bereits im Windows-Speicher installiert ist — etwa weil du eine qualifizierte eID-Karte mit Hersteller-Minidriver nutzt — verwende stattdessen TsgcWindowsStoreKeyProvider. Der Provider spricht CNG, sodass SHA-256/SHA-384 unabhängig vom ursprünglichen CSP funktionieren.

Match per Subject, Thumbprint oder Seriennummer. StoreLocation auf slCurrentUser nutzt den persönlichen Speicher; slLocalMachine nutzt den Maschinenspeicher und benötigt in der Regel Administratorrechte.

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

Das PAdES-B-T-Profil konfigurieren

PAdES-B-T fügt der Signatur einen RFC-3161-Zeitstempel hinzu, sodass Validatoren beweisen können, dass das Dokument vor einem bekannten Zeitpunkt signiert wurde — selbst wenn das Signaturzertifikat später abläuft oder widerrufen wird.

Profil-Eigenschaften

Level wählt das AdES-Level — palB, palT, palLT oder palLTA. Für PAdES-B-T setze es auf palT; das Profil erwartet dann eine nicht leere TSA.URL.

HashAlgorithm ist standardmäßig shaSHA256 — das, was jeder moderne Validator erwartet. SHA-384 und SHA-512 sind ebenfalls verfügbar; SHA-1 wird bewusst nicht angeboten, weil eIDAS es nicht mehr akzeptiert.

Das Unter-Objekt SignatureField steuert, wo das sichtbare Signatur-Widget im PDF platziert wird — Seitenzahl, Rechteck-Koordinaten und optionale Felder für Grund, Ort und Kontakt. Lass es leer für unsichtbare Signaturen (Standard).

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;

Das PDF signieren

Verdrahte Key Provider und Profil mit TsgcSignPDF und rufe dann SignFile mit Eingabe- und Ausgabedateinamen auf.

Vollständiger Signieraufruf

SignFile liest das Eingabe-PDF, berechnet den Dokument-Digest, ruft den Key Provider zur Signaturerzeugung auf, holt einen Zeitstempel von der TSA und schreibt das Ergebnis. Wenn das Eingabedokument bereits Signaturen enthält, hängt sgcSign ein inkrementelles Update an — die ursprünglichen Signaturen bleiben gültig.

Das Event OnProgress meldet jede Phase (Lesen, Hashen, Signieren, Zeitstempeln, Schreiben) und ist praktisch für Fortschrittsbalken bei mehreren MB großen Dateien.

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;

Das signierte PDF verifizieren

Ein signiertes Dokument nützt nur etwas, wenn es auch verifiziert. TsgcSignatureVerifier validiert die kryptografische Signatur, die Zertifikatskette und den eingebetteten Zeitstempel in einem einzigen Aufruf.

Das Ergebnis lesen

Die Methode VerifyFile liefert ein TsgcSignatureReport mit einer Aufschlüsselung pro Signatur zurück. Pro Eintrag erhältst du Status (svValid, svInvalid, svUnknown), den Subject des Signierenden, das tatsächlich erkannte AdES-Level, den Zeitstempelwert sowie etwaige Probleme mit Kette oder Sperrinformationen.

Vergleiche Report.Level mit deinem erwarteten Level — du wolltest palT, also bedeutet jede Verifikation, die palB meldet, dass die Zeitstempelanfrage stillschweigend fehlgeschlagen ist. Die TSA-URL ist der übliche Verdächtige.

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;

Was üblicherweise schiefgeht

Eine kurze Liste der Probleme, die wir am häufigsten sehen, wenn Entwickler ihr erstes PAdES-Dokument signieren.

TSA liefert HTTP 200, aber eine fehlerhafte Antwort

Einige TSAs sind nur über HTTPS erreichbar und lehnen einfache http://-URLs mit einem stillen Fehler ab. Wenn Report.Level als palB zurückkommt, obwohl du palT verlangt hast, wechsle die TSA-URL auf das https://-Äquivalent. Die meisten öffentlichen TSAs veröffentlichen beide Varianten.

Zertifikat hat keine Extended Key Usage

Testzertifikaten fehlt häufig die EKU id-kp-documentSigning. Adobe Reader zeigt eine Warnung an, auch wenn die kryptografische Signatur gültig ist. Für die Produktion fordere bei deiner CA ein Zertifikat mit expliziter Document-Signing-EKU an.

PDF/A-Dokumente benötigen PAdES-B-LT

PDF/A-2- und PDF/A-3-Archive verlangen, dass alle Sperrinformationen direkt im Dokument eingebettet sind. Verwende palLT oder palLTA — sgcSign holt zur Signaturzeit OCSP-Antworten und CRLs und bettet sie in das DSS-Dictionary ein.

Smartcard-Sessions laufen ab

Manche PKCS#11-Treiber schließen die Session nach wenigen Sekunden Inaktivität. Halte die Instanz von TsgcPKCS11KeyProvider über die gesamte Signieroperation am Leben und rufe Free erst auf, nachdem SignFile zurückgekehrt ist.

Wie es weitergeht

Langzeitarchivierung, Länderprofile oder das Signieren vom Desktop in einen zentralen Server verlagern.

21 Länderprofile

Einzeiler für VeriFactu, FatturaPA, KSeF, FACTUR-X und die EU-Arbeitsvertragsprofile — alle auf Basis derselben PAdES-/XAdES-Engine.

Mehr erfahren →

sgcSign-Server

Verlege das Signieren in einen selbstgehosteten Daemon — REST-API, GitHub-Actions-Integration, Authenticode und ClickOnce zusätzlich zu PAdES.

Mehr erfahren →

10 Key Provider

Über PFX und Windows-Store hinaus — Azure Trusted Signing, AWS KMS, Google KMS, HashiCorp Vault, Certum und CSC-v2-Cloud-Provider.

Mehr erfahren →

Bereit, dein erstes PDF zu signieren?

Lade die Testversion herunter, fahre dieses Tutorial gegen deine eigene Rechnung und lizenziere sgcSign, sobald du ausgelieferst.