Zum Inhalt springen
getnextpdf.com

Wie Signaturen in einem PDF liegen

Spec: ETSI EN 319 142-1Spec: RFC 5652

Eine PDF-Signatur wird nicht um die Datei herumgelegt. Sie ist in ihr eingebettet: ein Dictionary, das die Signatur benennt, und ein Digest, der über einen deklarierten Byte-Bereich berechnet wird, der den Signaturwert selbst bewusst auslässt. Diese Seite erklärt diesen Mechanismus und – ebenso wichtig – was er nicht verspricht.

„Das Dokument ist signiert“ ist ein Satz, auf den Menschen ihre Entscheidungen stützen. Sie knüpfen daran eine Zahlung, eine Freigabe, eine rechtliche Verpflichtung. Wenn Sie nicht genau wissen, welche Bytes eine Signatur abdeckt, können Sie nicht sagen, was ein gültiges Ergebnis tatsächlich beweist. Ein PDF kann eine vollkommen gültige Signatur tragen und einem Leser dennoch Inhalt anzeigen, den der Unterzeichner nie gesehen hat, weil dieser Inhalt nach dem Signieren hinzugefügt wurde, in einem Bereich, den die Signatur nie beansprucht hat. Zu wissen, wo die Autorität der Signatur beginnt und endet, macht den Unterschied zwischen einer belastbaren und einer bloß hoffnungsvollen Entscheidung aus.

  • Eine PDF-Signatur liegt in einem Signatur-Dictionary und einem Signaturfeld innerhalb des Dokuments, nicht als externer Umschlag.
  • Die signierten Bytes werden durch ein ByteRange-Array deklariert: zwei (offset, length)-Segmente, die zusammen die gesamte Datei abdecken – mit Ausnahme des hexadezimalen Signaturwerts im Contents-Eintrag.
  • Der Digest dieser beiden aneinandergefügten Segmente ist das, was die kryptografische Signatur tatsächlich schützt.
  • Alles, was später in einer neuen Revision angehängt wird, liegt außerhalb des ursprünglichen Byte-Bereichs. Die ursprüngliche Signatur bleibt gültig; sie hat über die neuen Bytes nie eine Aussage getroffen.
  • Eine Freigabesignatur und eine Zertifizierungssignatur unterscheiden sich im Umfang: Die Zertifizierung (DocMDP) schränkt ein, welche späteren Änderungen erlaubt sind; die Freigabe tut das nicht.

NextPDF baut die Signatur gemäß dem Modell des Formats in einer festen Reihenfolge auf, sodass der Byte-Bereich exakt und nicht bloß näherungsweise ist.

Wenn die Engine eine Signatur schreibt, reserviert sie zunächst einen Slot fester Größe für den Contents-Wert und schreibt einen ByteRange-Platzhalter fester Breite. Sie wartet, bis das vollständige Dokument geschrieben ist, einschließlich der Querverweistabelle und der End-of-File-Markierung. Erst dann berechnet sie die beiden realen Offsets, schreibt sie in den Platzhalter zurück, ohne ein einziges Byte zu verschieben, bildet den Hash über die beiden Segmente und legt das resultierende CMS-Objekt in den reservierten Slot. Der Platzhalter wird genau deshalb mit Nullen auf eine konstante Länge aufgefüllt, damit das Einsetzen der realen Zahlen die zu hashenden Bytes nicht verschieben kann. Dies ist die einzige Reihenfolge, die eine in sich konsistente Signatur erzeugt. Die Engine behandelt jeden Fehler in dieser Abfolge als harten Fehler und nicht als stillen Fallback.

Für das PDF-2.0-Profil ist das Signaturobjekt selbst eine detached CMS-SignedData-Struktur. Das PDF-Dictionary sagt, wo und wie; das CMS-Objekt trägt das Wer und den kryptografischen Nachweis.

  1. Step 1 of 4: ISO 32000-2 §12.8.1 — ByteRange digest & signature dictionary
  2. Step 2 of 4: ISO 32000-2 §12.8.3.3 — ETSI.CAdES.detached SubFilter
  3. Step 3 of 4: ETSI EN 319 142-1 PAdES baseline profile
  4. Step 4 of 4: RFC 5652 CMS SignedData in Contents
Wo eine PDF-Signatur definiert ist, vom Containerformat bis hinunter zum kryptografischen Objekt: ISO 32000-2 legt das Dictionary und den Byte-Range-Mechanismus fest, ETSI EN 319 142-1 profiliert ihn für PAdES, und RFC 5652 definiert das CMS-SignedData-Objekt, das in Contents abgelegt wird.

Sie bauen ein ByteRange nur selten von Hand. Der Zweck des Beispiels ist, die Form des Ergebnisses zu zeigen, damit sie wiedererkennbar ist, wenn Sie eine signierte Datei untersuchen.

<?php
declare(strict_types=1);
use NextPDF\Security\Signature\ByteRangeCalculator;
// Offsets the engine knows only after the whole PDF is written:
// $contentsStart — byte just before the '<' of the hex signature
// $contentsEnd — byte just after the '>' that closes it
// $fileLength — total file size in bytes
$range = ByteRangeCalculator::calculate(
contentsStart: $contentsStart,
contentsEnd: $contentsEnd,
fileLength: $fileLength,
);
// $range === [0, $contentsStart, $contentsEnd, $fileLength - $contentsEnd]
// Segment 1: file start → just before the signature value
// Segment 2: just after the signature value → end of file
// The signature value itself is the gap. It is never hashed.
$signedMessage = ByteRangeCalculator::extractSignedData($pdfBytes, $range);
// $signedMessage is segment 1 concatenated with segment 2 — exactly the
// bytes the cryptographic digest is computed over.

Die Lücke zwischen den beiden Segmenten ist der Signaturwert. Er kann nicht Teil seines eigenen Digests sein, weshalb der Bereich aus zwei Teilen besteht und nicht aus einem.

Die Falle ist der Glaube, eine gültige Signatur bedeute, dass die gesamte Datei, die Sie vor sich sehen, das ist, was signiert wurde. Das tut sie nicht. Sie bedeutet, dass die Bytes innerhalb des deklarierten Bereichs unversehrt sind. Eine spätere Revision kann legitimerweise Inhalt anhängen – eine zweite Signatur, Formulardaten, Validierungsmaterial – außerhalb dieses Bereichs. Die erste Signatur bleibt gültig, und sie sagt nichts über die Ergänzung aus. Ein korrekter Viewer teilt Ihnen mit, dass eine Signatur „das Dokument in dem Zustand, in dem es zum Zeitpunkt des Signierens vorlag“ abdeckt, nicht „jedes Byte auf dem Bildschirm“. Beide gleichzusetzen ist der Weg, auf dem ein signiertes Dokument unsignierten Inhalt erhält, der signiert aussieht.

Diese Seite erklärt die Struktur, nicht das Vertrauen. Ein korrekt gebildetes ByteRange und CMS-Objekt sagen Ihnen, dass die Bytes unversehrt sind und welcher Schlüssel sie signiert hat. Sie sagen für sich genommen nicht, ob dieser Schlüssel demjenigen gehört, den Sie vermuten, ob sein Zertifikat zum Zeitpunkt des Signierens gültig war oder ob es später widerrufen wurde. Das ist Aufgabe der Zertifikatspfad- und Widerrufsprüfung, behandelt in Eine Signatur richtig validieren. Diese Seite behandelt auch nicht, wann das Signieren mit irgendeiner unabhängigen Autorität erfolgte. Ein selbst behaupteter Signaturzeitpunkt ist keine vertrauenswürdige Zeit – siehe Zeitstempel und vertrauenswürdige Zeit. NextPDF baut die hier beschriebene Struktur; die Zertifikate, Vertrauensanker und die Zeitstempelautorität werden von Ihrer Bereitstellung geliefert, nicht von der Engine.

Was die Engine je nach Edition ausliefert, ist die Fähigkeit zum Aufbau der Struktur:

PAdES-Signaturstruktur (byte range, signature dictionary, detached CMS) — edition availability
EditionAvailability
Core

PAdES B-B: das Signatur-Dictionary, das ByteRange fester Breite und das auf dieser Seite beschriebene detached CMS-SignedData-Objekt.

Pro

Ergänzt PAdES B-T – einen vertrauenswürdigen Zeitstempel auf dem Signaturwert – aufbauend auf derselben Struktur.

Enterprise

Ergänzt die Langzeitprofile (B-LT, B-LTA): eingebettetes Validierungsmaterial und Dokumentzeitstempel, die auf demselben Byte-Range-Fundament aufsetzen.

  • Signatur-Dictionary – das PDF-Dictionary, das den Signatur-Handler, den SubFilter, das ByteRange und den Contents-Wert benennt.
  • ByteRange – ein Array von (offset, length)-Ganzzahlpaaren, das die exakten Bytes deklariert, die der Signatur-Digest abdeckt.
  • Contents – der hexadezimale Eintrag, der den Signaturwert enthält (für PDF 2.0 ein detached CMS-SignedData-Objekt); er ist von seinem eigenen Digest ausgeschlossen.
  • CMS SignedData – eine Struktur der Cryptographic Message Syntax (RFC 5652), die das Zertifikat des Unterzeichners und die Signatur-Bytes trägt.
  • PAdES – PDF Advanced Electronic Signatures: das ETSI-Profil von CMS-Signaturen für PDF, definiert in der Normenreihe ETSI EN 319 142.
  • Freigabesignatur – eine Signatur mit der MDP-Berechtigung 0; sie bestätigt den Inhalt, ohne spätere Änderungen einzuschränken.
  • Zertifizierungssignatur – eine Signatur mit einer DocMDP-Berechtigung (MDP 13), die begrenzt, welche späteren Modifikationen das Dokument konform halten.