Technical Article

Long-Term PDF Signatures in Delphi: PAdES B-LT and B-LTA

Damit sich eine signierte PDF-Datei auch in vielen Jahren noch verifizieren lässt (wenn das Signaturzertifikat abgelaufen ist und die ausstellende CA ihre Schlüssel erneuert hat), kann die PDFium-Komponente unter Windows Langzeit-PAdES-Signaturen erstellen: einen RFC-3161-Zeitstempel für eine vertrauenswürdige Zeitangabe, einen Document Security Store (DSS) zum Einbetten der Validierungsdaten und einen archivierenden DocTimeStamp über die gesamte Datei. Diese drei Ergänzungen entsprechen den PAdES-Baseline-Ebenen B-T, B-LT und B-LTA. Die PDFium-Komponente erreicht all diese Ebenen über einen einzigen Signier-Aufruf. Diese Funktion läuft ausschließlich unter Windows und ist an das Flag PadesCryptoAvailable gebunden

Dies ist der dritte Artikel in einer Reihe. Der erste, PDFs mit PAdES B-B signieren unter Verwendung von PDFium VCL, deckt die Basissignatur ab. Der zweite, PDF-Signaturen und PAdES-Ebenen prüfen, beschreibt das Auslesen einer Signatur und das Bestimmen ihres Niveaus. Hier geht es nun um die Archivierung: Signaturen, die den Ablauf von Zertifikaten überdauern und über Aufbewahrungsfristen von vielen Jahren hinweg verifizierbar bleiben müssen

Warum lässt sich eine gültige PDF-Signatur nach einiger Zeit nicht mehr verifizieren?

Eine Basissignatur beantwortet nur eine Frage: Wurden die Bytes nach dem Signieren verändert. Sie beweist für sich genommen nicht, wann die Signatur erstellt wurde, und diese Lücke bricht ihr später das Genick. Die CMS-Struktur enthält zwar ein Signaturzeit-Attribut, aber der Unterzeichner trägt diesen Wert von seiner eigenen Systemuhr ein, weshalb eine Prüfstelle diesem Wert nicht vertrauen kann. Wenn das Signaturzertifikat später abläuft oder von der CA widerrufen wird, kann eine strenge Prüfung nicht mehr unterscheiden, ob die Signatur erstellt wurde, als das Zertifikat noch gültig war, oder ob sie erst nach dessen Ablauf gefälscht wurde. Die Signatur war am Tag ihrer Erstellung in Ordnung und wird später unprüfbar, ohne dass ein Fehler des Anwenders vorliegt

Was ist eine LTV- oder Archiv-Zeitstempel-PDF-Signatur?

PAdES definiert vier aufeinander aufbauende Baseline-Ebenen, die die PDFium-Komponente über das Enum TPadesLevel bereitstellt (plB_B, plB_T, plB_LT, plB_LTA). B-B ist die einfache Signatur. B-T fügt einen vertrauenswürdigen Zeitstempel über den Signaturwert hinzu. B-LT (Long-Term) fügt die eingebetteten Validierungsdaten hinzu — die Eigenschaft, die meistens gemeint ist, wenn man von „LTV“ spricht. B-LTA (Long-Term mit Archiv-Zeitstempel) umschließt das Ganze mit einem weiteren Zeitstempel über die gesamte Datei. So wird bewiesen, dass die eingebetteten Validierungsdaten selbst seit dem Zeitpunkt der Archivierung unverändert geblieben sind

In der Praxis entspricht dies einer Treppe: B-T beweist wann. B-LT beweist, womit die Signatur auch in Zukunft noch geprüft werden kann. B-LTA beweist, dass das gesamte Paket seit seiner Versiegelung nicht manipuliert wurde. Dies ist die Ebene, die Sie erneuern (durch Hinzufügen eines frischen DocTimeStamp vor Ablauf des vorherigen Zeitstempels), um das Archiv unbegrenzt zu verlängern. Für Verträge oder Compliance-Dokumente mit langer Aufbewahrungspflicht ist B-LTA das Ziel, B-LT die pragmatische Untergrenze

Hinzufügen einer vertrauenswürdigen Zeitquelle: PAdES B-T mit RFC 3161

Die PDFium-Komponente wertet eine B-B-Signatur zu B-T auf, indem zwei Felder in TPadesSignOptions konfiguriert werden: Setzen Sie Level auf plB_T und geben Sie eine TsaUrl (URL einer Zeitstempelbehörde) an. Wenn beide vorhanden sind, hasht die Signier-Pipeline die rohen Signatur-Bytes mit SHA-256, verpackt diesen Hash als RFC-3161-messageImprint, sendet die Anfrage per POST über WinHttp an die TSA und bettet das zurückgegebene Token als nicht-signiertes Attribut signature-time-stamp (OID 1.2.840.113549.1.9.16.2.14) in die SignerInfo-Struktur unter unsignedAttrs [1] ein (gemäß EN 319 142-1 §6.3). Der Zeitstempel deckt gezielt den Signaturwert ab, was die vertrauenswürdige Zeit an genau diese Signatur bindet

uses
  PDFium, FPdfPades;

procedure SignWithTimestamp;
var
  Pdf: TPdf;
  Options: TPadesSignOptions;
begin
  Pdf := TPdf.Create(nil);
  try
    if not Pdf.PadesCryptoAvailable then
      raise Exception.Create('PAdES signing backend is Windows-only');
    Pdf.FileName := 'contract.pdf';
    Pdf.Active := True;

    Options := TPadesSignOptions.Default;
    Options.CertificateThumbprint := 'A1B2C3D4E5F6071829304152637485960A1B2C3D';
    Options.Level  := plB_T;
    Options.TsaUrl := 'http://timestamp.example-tsa.com/tsr';
    Options.Reason := 'Contract execution';
    // Nonce left at 0: the component derives a unique anti-replay value.

    Pdf.SignPades('contract-bt.pdf', Options);
  finally
    Pdf.Free;
  end;
end;

Der CertificateThumbprint ist der SHA-1-Hash (hexadezimal) eines Zertifikats im persönlichen Zertifikatsspeicher des aktuellen Benutzers („MY“), dessen privater Schlüssel genutzt werden kann. Das Feld Nonce ist der RFC-3161-Schutzwert gegen Replay-Angriffe. Belassen Sie es auf Null, leitet die Komponente einen eindeutigen Wert für jede Anfrage ab (aus einem SHA-256-Hash aus Zähler, Uhrzeit, Ticks und Prozess-ID), sodass zwei in derselben Millisekunde abgesetzte Anfragen nicht kollidieren. Geben Sie hier nur dann einen eigenen Wert an, wenn Sie einen kryptografischen Zufallszahlengenerator nutzen möchten. Ein wichtiger Hinweis: B-T benötigt eine erreichbare TSA, weshalb das Signieren nun einen Netzwerkaufruf auslöst und von der Verfügbarkeit sowie der Latenz der TSA abhängt

Einbetten von Validierungsdaten: B-LT und B-LTA in einem Aufruf

Der Schritt hin zur Langzeit-Validierung erfordert nur eine Änderung: Setzen Sie Level auf plB_LTA und behalten Sie die TsaUrl bei. Die PDFium-Komponente führt dann die gesamte Kette innerhalb eines einzigen SignPades-Aufrufs aus. Sie signiert B-B, fügt den Zeitstempel für B-T hinzu, injiziert den Document Security Store für B-LT und hängt den Archiv-Zeitstempel für B-LTA an — jeweils als eigenes inkrementelles Update, sodass die vorherigen Bytes vollständig unverändert bleiben

procedure SignForArchive;
var
  Pdf: TPdf;
  Options: TPadesSignOptions;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'contract.pdf';
    Pdf.Active := True;

    Options := TPadesSignOptions.Default;
    Options.CertificateThumbprint := 'A1B2C3D4E5F6071829304152637485960A1B2C3D';
    Options.Level    := plB_LTA;   // drives B-B -> B-T -> B-LT -> B-LTA
    Options.TsaUrl   := 'http://timestamp.example-tsa.com/tsr';
    Options.Location := 'Head Office';

    // One call writes the timestamp, the DSS and the archive DocTimeStamp.
    Pdf.SignPades('contract-lta.pdf', Options);
  finally
    Pdf.Free;
  end;
end;

Für die B-LT-Stufe baut die PDFium-Komponente die Zertifikatskette mit CertGetCertificateChain auf und schreibt ein /DSS-Verzeichnis, das das Zertifikat des Unterzeichners sowie alle Zwischenzertifikate (CAs) in einem /Certs-Array (mit parallelen /OCSPs- und /CRLs-Arrays) speichert. Dazu kommt ein /Extensions /ESIC-Marker für Level 1, genau wie es EN 319 142-1 §5.4.2 vorschreibt. Das selbstsignierte Stammzertifikat wird bewusst weggelassen, da RFC 5652 §10.2.3 dies erlaubt und das Stammzertifikat ohnehin als Vertrauensanker beim Prüfer vorliegen muss. Das Ergebnis ist eine Signatur, die ein PDF-Reader prüfen kann, ohne auf externe Server zugreifen zu müssen

Für B-LTA hängt die Komponente einen Dokumenten-Zeitstempel (DocTimeStamp) an: ein Signaturverzeichnis mit dem Typ /SubFilter /ETSI.RFC3161, dessen /ByteRange die gesamte Datei abdeckt. Der Eintrag /Contents speichert ein RFC-3161-Token über diesen Bereich und stuft den ESIC-Marker auf Level 2 hoch. Dies ist der Archivierungsschritt nach §5.4.3 und TS 119 142-3. Da der DocTimeStamp sowohl das DSS als auch die Signatur abdeckt, beweist er, dass die Validierungsdaten zum Zeitpunkt der Archivierung vorhanden und unverändert waren. Das ist genau das, was eine langfristige Aufbewahrungsrichtlinie fordern muss

Erweitern einer bereits vorhandenen Signatur

Manchmal befindet sich die Signatur bereits auf dem Dokument und Sie müssen nur den Validierungsspeicher (DSS) hinzufügen oder aktualisieren — beispielsweise, um eine von Dritten erstellte B-B-Signatur für Ihr Archiv auf B-LT aufzuwerten. Die PDFium-Komponente stellt die DSS-Injektion direkt über SaveAsPadesDss bereit, was die Strukturen /DSS und /Extensions als inkrementelles Update anhängt, ohne die vorhandenen Signatur-Bytes zu verändern

procedure AddValidationStore;
var
  Pdf: TPdf;
  DssOpts: TPadesDssOptions;
  Cert: TPadesDssMaterial;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'already-signed.pdf';
    Pdf.Active := True;

    DssOpts := TPadesDssOptions.Default;
    DssOpts.EsicExtensionLevel := 1;         // 1 for B-LT, 2 for B-LTA
    Cert.DerBytes := LoadSignerCertDer;       // your DER-encoded certificate
    SetLength(DssOpts.Certs, 1);
    DssOpts.Certs[0] := Cert;

    Pdf.SaveAsPadesDss('already-signed-lt.pdf', DssOpts);
  finally
    Pdf.Free;
  end;
end;

Sie übergeben die DER-codierten Zertifikate (und optional OCSP-Antworten sowie CRLs), die eine Prüfstelle zur Verifizierung benötigt. Setzen Sie EsicExtensionLevel auf 1 für ein B-LT-Archiv oder auf 2, falls ein DocTimeStamp folgen soll. Belassen Sie IncludeVri auf dem Standardwert False: EN 319 142-1 §5.4.2.3 behandelt das signaturspezifische /VRI-Verzeichnis als optional und rät davon ab, es sei denn, ein von Ihnen anvisierter Reader verlangt dies explizit

Einschränkungen und Aufwände bei der Planung

Drei Einschränkungen bestimmen den Einsatz dieser Stufen. Erstens läuft das kryptografische Backend nur unter Windows: Das Signieren und Zeitstempeln nutzt die CNG- und WinHttp-Bibliotheken des Betriebssystems, weshalb PadesCryptoAvailable auf anderen Plattformen False zurückgibt und SignPades eine Exception auslöst. Das im Begleitartikel behandelte Auslesen und Prüfen von Signaturen bleibt plattformübergreifend verfügbar. Nur das Erstellen von Signaturen ist an Windows gebunden. Zweitens hängen B-T und B-LTA von einer Zeitstempelbehörde ab, was beim Signieren einen Netzwerkaufruf bedeutet. Die Verfügbarkeit und die Ratenbegrenzungen des Drittanbieters werden somit Teil Ihres Signierpfades

Drittens kostet jede Ebene Speicherplatz. Da jede Stufe ein inkrementelles Update anhängt, statt die Datei neu zu schreiben, das DSS eine vollständige Zertifikatskette einbettet und der Archiv-Zeitstempel Platz für sein Token reserviert, ist eine B-LTA-Datei spürbar größer als das B-B-Original. Das ist der bewusste Kompromiss: Sie investieren Bytes, um eine langfristige Verifizierbarkeit zu sichern. Für die meisten Verträge und Compliance-Archive ist dies der richtige Weg. Es empfiehlt sich jedoch, dies an eigenen Dokumenten zu messen. Wenn Sie prüfen möchten, ob eine fertige Datei die gewünschte Ebene erreicht hat, können Sie dies mit den im Artikel PDF-Signaturen und PAdES-Ebenen prüfen beschriebenen Techniken tun. Falls Sie eine Dokumenten-Pipeline absichern möchten, beschreibt der Artikel über PDF-Sicherheitsaudits weitere Kontrollen

Die hier gezeigten PAdES-Signier-, DSS- und Archiv-Zeitstempel-Funktionen sind Teil der PDFium-Komponente für Delphi und C++Builder, deren Produktseite die vollständige Signatur-Referenz und die Plattformanforderungen auflistet