Zum Inhalt springen

Verschlüsselung: AES-256 (CBC) und AES-256-GCM

Core verschlüsselt PDFs mit AES-256 unter dem Standard-Security-Handler aus ISO 32000-2:2020 §7.6. Standardmäßig nutzt Core V=5 / R=6 / AESV3 (AES-256-CBC). Optional steht der ISO/TS 32003:2023 V=6 / R=7 AES-256-GCM-Pfad mit authentifizierter Verschlüsselung zur Verfügung. Diese Seite dokumentiert die Schlüsselableitung, das Drahtformat, die Berechtigungsgrenze und die Einschränkungen, die ein Deployment kennen muss.

Terminal-Fenster
composer require nextpdf/core:^3

Der Standardpfad benötigt die openssl-Erweiterung. Der AES-256-GCM-Pfad nutzt openssl oder ext-sodium. Auf einem Host ohne AES-NI-Hardware verweigert libsodium GCM; Core fällt dann auf die langsamere OpenSSL-Implementierung zurück, anstatt den Algorithmus abzuschwächen.

Der Standard-Handler ist der Standard-Security-Handler V=5 / R=6 mit dem AESV3-Crypt-Filter. Bei setEncryption() erzeugt Core einen zufälligen 256-Bit-Dateischlüssel aus der kryptografischen Zufallsquelle der Plattform (random_bytes()). Die Byte-Länge beträgt 32 Byte und entspricht der Schlüssellänge aus FIPS 197. Der Inhalt jedes Objekts wird mit AES-256-CBC verschlüsselt. Der 16-Byte-Initialisierungsvektor wird jedem Geheimtext vorangestellt, wie es ISO 32000-2:2020 §7.6.4 vorgibt.

Die Schlüsselableitung folgt Algorithmus 2.B in Revision 6. Ein Passwort wird zunächst mit SASLprep (RFC 4013) normalisiert und dann an einer Zeichengrenze auf 127 UTF-8-Byte gekürzt, wie es ISO 32000-2:2020 §7.6.4.3.3 vorgibt. Der abgeleitete Hashwert wird mit einer iterierten SHA-256- / SHA-384- / SHA-512-Routine berechnet, die von einem AES-128-CBC-Schritt angetrieben wird und die Kosten für Offline-Passwortraten erhöht. Die Salts für Benutzer, Eigentümer und Schlüssel werden einmal pro Encryptor-Instanz erzeugt, sodass eine einzelne Instanz deterministische Dictionary-Bytes ausgibt — eine Voraussetzung für einen Mehrpass-Writer.

useAesGcm() aktiviert den optionalen AES-256-GCM-Pfad. Er implementiert den ISO/TS 32003:2023 V=6 / R=7 AESV4-Crypt-Filter. Die Chiffre ist AES-256-GCM mit Parametern aus NIST SP 800-38D. Das Drahtformat jedes verschlüsselten Objekts besteht aus einem 12-Byte-IV, dem Geheimtext und anschließend einem 16-Byte-Authentifizierungs-Tag. Zusätzliche authentifizierte Daten bleiben leer, wie es das Profil aus TS 32003 §5.2 vorgibt. Die Entschlüsselung prüft das Tag und löst bei einer Abweichung TamperedDataException aus; sie gibt für ein fehlgeschlagenes Tag niemals Klartext zurück. Damit ergänzt dieser Pfad eine Manipulationserkennung, die der Standard-CBC-Pfad allein nicht bietet.

Der GCM-Pfad folgt den Vorgaben zur IV-Eindeutigkeit aus NIST SP 800-38D §8. Die oberen 4 Byte des IV sind ein pro Instanz festes Feld, das bei der Konstruktion aus einer Zufallsquelle initialisiert wird. Die unteren 8 Byte sind ein Big-Endian-Zähler, der nach jedem ausgegebenen IV hochzählt. Das entspricht dem deterministischen Konstruktionsansatz aus §8.2.1, mit dem Unterschied, dass das feste Feld zufällig gewählt wird, um eine dokumentübergreifende Kollision zu verhindern, anstatt es fortlaufend zu nummerieren. Als zusätzliche Absicherung vermerkt Core jeden ausgegebenen IV in einer Kollisionsmenge und löst NonceReuseException aus, falls sich ein Wert wiederholt. Ein Zählerüberlauf löst ebenfalls NonceReuseException aus, denn der Überlauf ist der IV-Wiederverwendungs-Fehlerfall, vor dem §8 warnt.

Auf dem GCM-Pfad gelten zwei Längengrenzen. Die Klartext-Obergrenze pro Objekt liegt bei 2^39 − 256 Byte und entspricht der in NIST SP 800-38D §5.2.1.1 abgeleiteten Grenze pro Aufruf. Eine größere Eingabe löst eine Längen-Exception aus und weist darauf hin, den Inhalt auf mehrere Objekte aufzuteilen. Die Sicherheitsgrenze liegt bei 2^32 Aufrufen pro Schlüssel. assertWithinSafetyBound() ist eine optionale Prüfung, die GcmInvocationLimitExceededException auslöst, damit ein Aufrufer den Dokumentschlüssel vor der Schwelle aus §8.3 rotiert. NIST SP 800-57 Teil 1 §4 beschreibt diese Entscheidung zur Schlüssellebensdauer als Verantwortung des Deployments.

Berechtigungsflags sind nur Empfehlungen. Die Bitmaske wird in den verschlüsselten /Perms-Eintrag und den /P-Wert geschrieben und beim Lesen mit validatePerms() wiederhergestellt; bei einem beschädigten Marker schlägt dies fehl. Von einem konformen Reader wird erwartet, dass er die Flags beachtet. Die Flags werden nicht kryptografisch erzwungen: Ein Prozessor, der den Entschlüsselungsschlüssel besitzt und die Bits ignoriert, kann den Inhalt lesen, kopieren oder ändern. Beschreiben Sie Berechtigungsflags als Reader-Konvention, nicht als Zugriffssteuerung.

TypArtWichtige MitgliederStabilitätSeit
Aes256Encryptorclassencrypt(), decrypt(), encryptForObject(), buildEncryptionDictionary(), verifyUserPassword(), verifyOwnerPassword(), validatePerms(), getEncryptionKey()stable1.0.0
Aes256GcmEncryptorclassencrypt(), decrypt(), encryptStream(), assertWithinSafetyBound(), invocationCount(), isAvailable()stable2.18.0
KeyMaterialfinal readonly classgenerate(), exposeKey(), fingerprint()stable2.18.0
EncryptedPayloadSpecfinal readonly classtoDict()stable2.18.0
CryptoCapabilitiesfinal classhasAesGcm(), detectFipsMode(), assertFipsAvailableForProfile()stable2.0.0
NonceReuseExceptionexceptionstable2.18.0
TamperedDataExceptionexceptionstable2.18.0
DecryptionFailedExceptionexceptionstable2.18.0
GcmInvocationLimitExceededExceptionexceptionstable3.0.0
examples/22-protection.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
// AES-256-CBC, V=5/R=6. Call before addPage().
$doc->setEncryption(
userPassword: 'demo',
ownerPassword: 'admin',
permissions: 4, // printing only; copy/modify denied for a conforming reader
);
$doc->addPage();
$doc->setFont('helvetica', '', 12);
$doc->cell(0, 8, 'Confidential', newLine: true);
$doc->save(__DIR__ . '/output/22-protection.pdf');
examples/security/gcm-authenticated-encryption.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Security\CryptoCapabilities;
use NextPDF\Security\Encryption\Aes256GcmEncryptor;
use NextPDF\Security\Exception\TamperedDataException;
use NextPDF\Security\KeyMaterial;
use Psr\Log\LoggerInterface;
final readonly class AuthenticatedBlobCipher
{
public function __construct(private LoggerInterface $logger) {}
/**
* Seal a payload with AES-256-GCM and return the wire-format bytes.
*
* @param non-empty-string $plaintext The payload to protect.
*
* @return non-empty-string IV(12) || ciphertext || tag(16).
*/
public function seal(string $plaintext, KeyMaterial $key): string
{
if (!CryptoCapabilities::hasAesGcm()) {
throw new \RuntimeException('Host cannot perform AES-256-GCM.');
}
$cipher = new Aes256GcmEncryptor($key);
// Opt-in NIST SP 800-38D §8.3 key-rotation guard.
$cipher->assertWithinSafetyBound();
$wire = $cipher->encrypt($plaintext);
$this->logger->info('Payload sealed', [
'key_fingerprint' => $key->fingerprint(),
'invocations' => $cipher->invocationCount(),
]);
return $wire;
}
/**
* Open a sealed payload; a modified payload raises, never returns plaintext.
*
* @param non-empty-string $wire IV(12) || ciphertext || tag(16).
*/
public function open(string $wire, KeyMaterial $key): string
{
try {
return (new Aes256GcmEncryptor($key))->decrypt($wire);
} catch (TamperedDataException $e) {
$this->logger->warning('Tampered payload rejected', [
'key_fingerprint' => $key->fingerprint(),
]);
throw $e;
}
}
}

Die Chiffre prüft die Host-Fähigkeit, wendet den optionalen Aufrufschutz an, protokolliert nur den nicht umkehrbaren Schlüssel-Fingerprint und reicht eine Manipulationsablehnung weiter, statt verdächtige Bytes zurückzugeben.

  • Der Standard-AES-256-CBC-Pfad bietet nur Vertraulichkeit. Er erkennt einen veränderten Geheimtext nicht selbst. Verwenden Sie den AES-256-GCM-Pfad, wenn Sie eine Manipulationserkennung benötigen.
  • useAesGcm() löst eine Exception aus, wenn der PDF/A-Modus aktiv ist, und ebenso, wenn weder openssl noch ext-sodium AES-256-GCM bietet. Fangen Sie beide Fälle ab und geben Sie eine für Betreiber umsetzbare Meldung aus.
  • Auf einem Host ohne AES-NI verweigert libsodium GCM. Core fällt auf OpenSSL-GCM zurück, das korrekt arbeitet, aber langsamer ist; der Durchsatz sinkt, die Sicherheit nicht.
  • Die GCM-Klartext-Obergrenze pro Objekt liegt bei 2^39 − 256 Byte. Eine größere Eingabe löst eine Längen-Exception aus; teilen Sie den Inhalt mit encryptStream() auf mehrere Objekte auf.
  • Eine KeyMaterial-Instanz muss genau 32 Byte lang sein; eine falsche Länge wird bei der Konstruktion abgelehnt und nicht abgeschnitten.
  • Der Lesepfad (verifyUserPassword(), verifyOwnerPassword(), validatePerms()) nutzt einen konstantzeitigen Vergleich für kryptografisches Material und schlägt bei einem beschädigten Berechtigungsmarker fehl.

Die AES-256-CBC-Verschlüsselung pro Objekt erfolgt in einem OpenSSL-Aufruf und ist O(n) im Objektkörper. Die Schlüsselableitung führt die iterierte Routine aus Algorithmus 2.B einmal pro Encryptor-Instanz aus; die Kosten sind begrenzt und pro Dokument konstant. Der AES-256-GCM-Streaming-Pfad teilt die Eingabe in 16-MiB-Chunks auf, was den aktiven Heap unabhängig von der Gesamtgröße der Eingabe auf rund 64 MB begrenzt und damit deutlich unter dem dokumentierten 64-MB-Spitzenbudget hält. Jedes GCM-Objekt fügt 28 Byte Overhead hinzu (12-Byte-IV plus 16-Byte-Tag). AES-NI-Hardware verbessert den GCM-Durchsatz spürbar; ihr Fehlen senkt nur den Durchsatz.

Das Bedrohungsmodell für diese Oberfläche ist ausdrücklich definiert. Das Offline-Raten von Passwörtern wird durch die SASLprep-Normalisierung plus die iterierte Schlüsselableitung aus Revision 6 verteuert, aber ein schwaches Passwort bleibt das dominante Restrisiko. Keine Ableitung kann es beseitigen. Eine Veränderung des Geheimtexts wird auf dem GCM-Pfad über die Tag-Prüfung erkannt und auf dem Standard-CBC-Pfad nicht erkannt. Eine IV-Wiederverwendung auf dem GCM-Pfad wird durch einen Zähler plus eine Kollisionsmenge verhindert, im Einklang mit der IV-Disziplin aus NIST SP 800-38D §8.1. Bei einem Zählerüberlauf wird verweigert, statt umzulaufen. Eine Schlüsseloffenlegung über Logs wird durch die Maskierung in KeyMaterial und das #[\SensitiveParameter]-Attribut auf Passwörtern gemildert. Abgeleitetes Schlüsselmaterial wird nach der Nutzung mit Nullen überschrieben, wo die Plattform es zulässt.

Auch die Grenze ist ausdrücklich benannt. Die AES-256-Verschlüsselung wird gemäß ISO 32000-2:2020 §7.6 und, für den optionalen Pfad, ISO/TS 32003:2023 §5.2 angewandt; der tatsächliche Schutz hängt von der Passwortstärke, dem Schlüsselmanagement, der Deployment-Umgebung und dem konsumierenden Reader ab. Berechtigungsflags werden von konformen Readern beachtet und nicht kryptografisch erzwungen. Der AES-ECB-Schritt, der für den /Perms-Wert genutzt wird, ist in ISO 32000-2:2020 §7.6.4.4.10 für einen einzelnen 16-Byte-Block vorgeschrieben. Er ist kein Allzweckmodus. Die Schlüsselrotation vor der 2^32-Aufrufgrenze liegt in der Verantwortung des Deployments; Core stellt dafür eine Prüfung bereit, erzwingt sie aber standardmäßig nicht.

Verschlüsselung und Entschlüsselung laufen im Prozess; keine Dokument-Bytes, kein Passwort und kein Schlüsselwert verlassen über diese Oberfläche den Host. Die GCM-IV-Kollisionsmenge ist nach einem nicht umkehrbaren Schlüssel-Fingerprint indiziert, nicht nach Schlüssel-Bytes. Ein Deployment, das den Schlüssel in ein externes Schlüsselmanagement oder einen PKCS#11-Token auslagert, ist für die Residenz dieses Backends verantwortlich; OASIS PKCS#11 v3.1 C_GenerateKey ist der Vertragspunkt für die token-residente Schlüsselerzeugung.

Protokollieren Sie den Policy-Namen und den 8-stelligen Schlüssel-Fingerprint, niemals den Schlüssel oder das Passwort. KeyMaterial::__toString() und __debugInfo() geben einen maskierten Platzhalter zurück. Exception-Meldungen von dieser Oberfläche enthalten ein Operationslabel und einen Fingerprint, keine Schlüssel-Bytes. Die GCM-Aufrufzahl ist ein sicheres Telemetrie-Signal für Dashboards zur Schlüsselrotation.

BedrohungMinderung in CoreRestgrenze
Offline-Raten von PasswörternSASLprep plus iterierte Ableitung aus Revision 6Ein schwaches Passwort ist weiterhin das dominante Risiko
Veränderung des GeheimtextsGCM-Tag-Prüfung (optionaler Pfad)Der CBC-Pfad bietet nur Vertraulichkeit
IV-Wiederverwendung (GCM)Zufälliges festes Feld plus Zähler plus Kollisionsmenge; Überlauf wird verweigert
Zu langer GCM-KlartextLängenprüfung bei 2^39 − 256; Hinweis zum AufteilenDer Aufrufer muss große Eingaben streamen
Schlüsselübernutzung (GCM)assertWithinSafetyBound() bei 2^32Optional; standardmäßig nicht erzwungen
Umgehung von BerechtigungsflagsKeine — die Flags sind nur EmpfehlungenEin nicht konformer Reader ignoriert die Flags
Schlüsseloffenlegung über LogsMaskierung durch KeyMaterial; #[\SensitiveParameter]Ein Aufrufer, der exposeKey() protokolliert, hebelt das aus

Core ist kein FIPS-validiertes kryptografisches Modul und ist nicht FIPS-zertifiziert. CryptoCapabilities::detectFipsMode() ist eine Best-Effort-Prüfung, die aktiv, nicht vorhanden oder unbestimmt zurückmeldet, und assertFipsAvailableForProfile() schlägt fehl, wenn ein FIPS-Profil auf einem Host ausgewählt wird, der keinen FIPS-Provider nachweist. Die Verschlüsselungsoberfläche arbeitet in einem FIPS-kompatiblen Modus, wenn sie gegen ein Host-OpenSSL-Build läuft, das einen FIPS-validierten Provider geladen hat. Eine validierte, zertifizierte Bereitstellung ist ein Enterprise-Thema.

AussageStandardKlauselNachweis
Jeder GCM-IV ist pro Aufruf eindeutig über eine deterministische Konstruktion aus festem Feld plus Zähler.NIST SP 800-38D§8.2.1
Die Disziplin bei der IV-Konstruktion verhindert eine Wiederverwendung über Aufrufe hinweg für einen Schlüssel.NIST SP 800-38D§8.1
Die Klartext-Obergrenze pro Objekt entspricht der Längengrenze pro Aufruf.NIST SP 800-38D§5.2.1.1
Kryptoperiode und Schlüsselrotation liegen in der Verantwortung des Deployments.NIST SP 800-57 Teil 1 Rev. 5§4
Der AES-Dateischlüssel ist 256 Bit lang und entspricht der Schlüssellänge des Standards.FIPS 197§4.2.1
Die token-residente Schlüsselerzeugung ist der Integrationspunkt für den externen Schlüsselspeicher.OASIS PKCS#11 v3.1C_GenerateKey

ISO 32000-2:2020 §7.6 und ISO/TS 32003:2023 §5.2 sind die normative Grundlage für die hier dokumentierten Handler. Ihr Text ist lizenzbeschränkt. Diese Seite paraphrasiert sie, zitiert die Klausel mit ihrer Nummer und gibt keinen dieser Texte wörtlich wieder. Der verifizierte Laufzeitnachweis für die bytegenaue Schlüsselableitung ist der Standardtest zu Algorithmus 2.B und das External-Oracle-Fixture, die im Nachweis-Trailer der Seite aufgeführt sind.

Core liefert sowohl den Standard-AES-256-CBC-Pfad als auch den optionalen AES-256-GCM-Pfad mit einer lokalen Schlüsseloberfläche und dem Gate der Krypto-Policy. Die Enterprise-Edition ergänzt über dieselben Verträge ein HSM/PKCS#11-Backend zur Schlüsselverwahrung und ein FIPS-Modus-Profil für die Krypto-Policy. Die öffentliche API ist identisch; das Backend zur Schlüsselverwahrung und die Policy-Implementierung unterscheiden sich.