Chiffrement : AES-256 (CBC) et AES-256-GCM
En un coup d’œil
Section intitulée « En un coup d’œil »Core chiffre les PDF avec AES-256 sous le gestionnaire de sécurité Standard de l’ISO 32000-2:2020 §7.6. Le mode par défaut est V=5 / R=6 / AESV3 (AES-256-CBC). Un mode activable fournit le chemin authentifié AES-256-GCM V=6 / R=7 de l’ISO/TS 32003:2023. Cette page documente la dérivation de clé, le format de sérialisation, la frontière des permissions et les limites qu’un déploiement doit maîtriser.
Installation
Section intitulée « Installation »composer require nextpdf/core:^3Le chemin par défaut requiert l’extension openssl. Le chemin AES-256-GCM utilise openssl ou ext-sodium. Sur un hôte sans matériel AES-NI, libsodium refuse GCM et Core se replie sur l’implémentation OpenSSL plus lente plutôt que de dégrader l’algorithme.
Vue d’ensemble conceptuelle
Section intitulée « Vue d’ensemble conceptuelle »Le gestionnaire par défaut est le gestionnaire de sécurité Standard V=5 / R=6 avec le filtre de chiffrement AESV3. À l’appel de setEncryption(), Core génère une clé de fichier aléatoire de 256 bits à partir de la source d’aléa cryptographique de la plateforme (random_bytes()). La clé mesure 32 octets, ce qui correspond à la longueur de clé FIPS 197. Le contenu est chiffré objet par objet avec AES-256-CBC. Le vecteur d’initialisation de 16 octets est préfixé à chaque texte chiffré, comme l’impose l’ISO 32000-2:2020 §7.6.4.
La dérivation de clé suit l’algorithme 2.B pour la révision 6. Le mot de passe est d’abord normalisé avec SASLprep (RFC 4013), puis tronqué à 127 octets UTF-8 sur une frontière de caractère, comme l’impose l’ISO 32000-2:2020 §7.6.4.3.3. Le condensat dérivé est calculé par une routine itérée SHA-256 / SHA-384 / SHA-512 pilotée par une étape AES-128-CBC, ce qui renchérit une attaque hors ligne visant à deviner le mot de passe. Les sels utilisateur, propriétaire et par clé sont générés une seule fois par instance de chiffreur, de sorte qu’une même instance émet des octets de dictionnaire déterministes — condition préalable pour un rédacteur multi-passes.
useAesGcm() active le chemin AES-256-GCM activable. Il met en œuvre le filtre de chiffrement AESV4 V=6 / R=7 de l’ISO/TS 32003:2023. L’algorithme de chiffrement est AES-256-GCM avec les paramètres du NIST SP 800-38D. La sérialisation de chaque objet chiffré contient un IV de 12 octets, le texte chiffré, puis une étiquette d’authentification de 16 octets. Les données authentifiées additionnelles sont vides, comme l’impose le profil §5.2 de la TS 32003. Le déchiffrement vérifie l’étiquette et lève TamperedDataException en cas de non-correspondance ; il ne renvoie jamais de texte en clair si l’étiquette échoue. Ce chemin ajoute une détection de modification que le chemin CBC par défaut ne fournit pas à lui seul.
La discipline d’unicité de l’IV sur le chemin GCM suit le NIST SP 800-38D §8. Les 4 octets de poids fort de l’IV constituent un champ fixe par instance, défini à partir d’une source aléatoire lors de la construction. Les 8 octets de poids faible sont un compteur big-endian qui s’incrémente après chaque IV émis. Cela correspond à l’approche de construction déterministe du §8.2.1, sauf que le champ fixe est aléatoire pour éviter les collisions inter-documents plutôt qu’énuméré. Une garde supplémentaire enregistre chaque IV émis dans un ensemble de collision et lève NonceReuseException si une valeur se répète. Le débordement du compteur lève aussi NonceReuseException, car il s’agit du mode de défaillance par réutilisation d’IV contre lequel le §8 met en garde.
Deux bornes de longueur s’appliquent au chemin GCM. Le plafond de texte en clair par objet est de 2^39 − 256 octets, soit la borne par invocation dérivée dans le NIST SP 800-38D §5.2.1.1. Une entrée plus grande lève une exception de longueur avec des indications pour partitionner le contenu entre plusieurs objets. La borne de sécurité d’invocation est de 2^32 appels par clé. assertWithinSafetyBound() est une vérification activable qui lève GcmInvocationLimitExceededException afin qu’un appelant fasse tourner la clé du document avant le seuil du §8.3. Le NIST SP 800-57 Partie 1 §4 présente cette décision de durée de vie de clé comme relevant de la responsabilité du déploiement.
Les drapeaux de permission sont indicatifs. Le masque de bits est écrit dans l’entrée chiffrée /Perms et dans la valeur /P, puis récupéré avec validatePerms() à la lecture, ce qui échoue en mode fermé sur un marqueur corrompu. Un lecteur conforme est censé respecter les drapeaux. Les drapeaux ne sont pas imposés par la cryptographie : un processeur qui détient la clé de déchiffrement et ignore les bits peut lire, copier ou modifier le contenu. Décris les drapeaux de permission comme une convention de lecteur, et non comme un contrôle d’accès.
Surface d’API
Section intitulée « Surface d’API »| Type | Catégorie | Membres clés | Stabilité | Depuis |
|---|---|---|---|---|
Aes256Encryptor | classe | encrypt(), decrypt(), encryptForObject(), buildEncryptionDictionary(), verifyUserPassword(), verifyOwnerPassword(), validatePerms(), getEncryptionKey() | stable | 1.0.0 |
Aes256GcmEncryptor | classe | encrypt(), decrypt(), encryptStream(), assertWithinSafetyBound(), invocationCount(), isAvailable() | stable | 2.18.0 |
KeyMaterial | classe final readonly | generate(), exposeKey(), fingerprint() | stable | 2.18.0 |
EncryptedPayloadSpec | classe final readonly | toDict() | stable | 2.18.0 |
CryptoCapabilities | classe final | hasAesGcm(), detectFipsMode(), assertFipsAvailableForProfile() | stable | 2.0.0 |
NonceReuseException | exception | — | stable | 2.18.0 |
TamperedDataException | exception | — | stable | 2.18.0 |
DecryptionFailedException | exception | — | stable | 2.18.0 |
GcmInvocationLimitExceededException | exception | — | stable | 3.0.0 |
Exemple de code — Démarrage rapide
Section intitulée « Exemple de code — Démarrage rapide »<?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');Exemple de code — Production
Section intitulée « Exemple de code — Production »<?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; } }}Le chiffreur vérifie la capacité de l’hôte, applique la garde d’invocation activable, ne journalise que l’empreinte non réversible de la clé et signale un rejet pour altération plutôt que de renvoyer des octets suspects.
Cas limites et pièges
Section intitulée « Cas limites et pièges »- Le chemin AES-256-CBC par défaut assure uniquement la confidentialité. Il ne détecte pas à lui seul un texte chiffré modifié. Utilise le chemin AES-256-GCM quand tu as besoin de détecter les modifications.
useAesGcm()lève une exception lorsque le mode PDF/A est actif et lorsque niopensslniext-sodiumne fournit AES-256-GCM. Intercepte les deux cas et fais remonter un message exploitable par l’opérateur.- Sur un hôte sans AES-NI, libsodium refuse GCM. Core se replie sur OpenSSL GCM, qui est correct mais plus lent ; le débit baisse, la sécurité ne baisse pas.
- Le plafond de texte en clair par objet GCM est de
2^39 − 256octets. Une entrée plus grande lève une exception de longueur ; partitionne le contenu entre plusieurs objets avecencryptStream(). - Une instance
KeyMaterialdoit mesurer exactement 32 octets ; une longueur incorrecte est rejetée à la construction, et non tronquée. - Le chemin de lecture (
verifyUserPassword(),verifyOwnerPassword(),validatePerms()) utilise une comparaison en temps constant sur le matériel cryptographique et échoue en mode fermé sur un marqueur de permission corrompu.
Performance
Section intitulée « Performance »Le chiffrement AES-256-CBC par objet correspond à un seul appel OpenSSL, en O(n) sur le corps de l’objet. La dérivation de clé exécute l’algorithme 2.B itéré une seule fois par instance de chiffreur ; le coût est borné et constant par document. Le chemin de flux AES-256-GCM partitionne l’entrée en blocs de 16 Mio, ce qui borne le tas vif à environ 64 Mo quelle que soit la taille totale de l’entrée, bien en deçà du budget de pic documenté de 64 Mo. Chaque objet GCM ajoute 28 octets de surcoût (IV de 12 octets plus étiquette de 16 octets). Le matériel AES-NI améliore sensiblement le débit GCM ; son absence ne fait que réduire le débit.
Notes de sécurité
Section intitulée « Notes de sécurité »Le modèle de menace de cette surface est explicite. L’attaque hors ligne visant à deviner le mot de passe est rendue plus coûteuse par la normalisation SASLprep et la dérivation de clé itérée de révision 6, mais un mot de passe faible reste le risque résiduel dominant. Aucune dérivation ne le supprime. La modification du texte chiffré est détectée sur le chemin GCM par la vérification de l’étiquette et n’est pas détectée sur le chemin CBC par défaut. La réutilisation d’IV sur le chemin GCM est empêchée par un compteur et un ensemble de collision, conformément à la discipline d’IV du NIST SP 800-38D §8.1. Le débordement du compteur refuse plutôt que de boucler. La divulgation de clé via les journaux est atténuée par la rédaction de KeyMaterial et par l’attribut #[\SensitiveParameter] sur les mots de passe. Le matériel de clé dérivé est mis à zéro après usage là où la plateforme le permet.
La frontière est tout aussi explicite. Le chiffrement AES-256 est appliqué tel que défini dans l’ISO 32000-2:2020 §7.6 et, pour le chemin activable, l’ISO/TS 32003:2023 §5.2 ; la protection effective dépend de la robustesse du mot de passe, de la gestion des clés, de l’environnement de déploiement et du lecteur consommateur. Les drapeaux de permission sont respectés par les lecteurs conformes et ne sont pas imposés par la cryptographie. L’étape AES-ECB utilisée pour la valeur /Perms est imposée par l’ISO 32000-2:2020 §7.6.4.4.10 pour un unique bloc de 16 octets. Ce n’est pas un mode à usage général. La rotation de clé avant la borne d’invocation de 2^32 est une responsabilité du déploiement ; Core expose une vérification à cet effet, mais ne l’impose pas par défaut.
Résidence des données et atténuations des données personnelles
Section intitulée « Résidence des données et atténuations des données personnelles »Le chiffrement et le déchiffrement s’exécutent dans le processus ; aucun octet de document, mot de passe ou valeur de clé ne quitte l’hôte par cette surface. L’ensemble de collision d’IV de GCM s’indexe sur une empreinte de clé non réversible, et non sur les octets de la clé. Un déploiement qui place la clé derrière une gestion de clés externe ou un jeton PKCS#11 est responsable de la résidence de ce backend ; v3.1 C_GenerateKey d’OASIS PKCS#11 est le point de contrat pour la génération de clé résidente dans le jeton.
Télémétrie sûre et nettoyage des journaux
Section intitulée « Télémétrie sûre et nettoyage des journaux »Journalise le nom de la politique et l’empreinte de clé de 8 caractères, jamais la clé ni le mot de passe. KeyMaterial::__toString() et __debugInfo() renvoient un substitut expurgé. Les messages d’exception de cette surface portent une étiquette d’opération et une empreinte, pas les octets de la clé. Le compteur d’invocations GCM est un signal de télémétrie sûr pour les tableaux de bord de rotation des clés.
Modèle de menace
Section intitulée « Modèle de menace »| Menace | Atténuation dans Core | Frontière résiduelle |
|---|---|---|
| Attaque hors ligne par devinette de mot de passe | SASLprep plus dérivation itérée de révision 6 | Un mot de passe faible reste le risque dominant |
| Modification du texte chiffré | Vérification de l’étiquette GCM (chemin activable) | Le chemin CBC assure uniquement la confidentialité |
| Réutilisation d’IV (GCM) | Champ fixe aléatoire plus compteur plus ensemble de collision ; le débordement refuse | — |
| Texte en clair GCM trop long | Vérification de longueur à 2^39 − 256 ; indications de partitionnement | L’appelant doit diffuser en flux les grandes entrées |
| Surutilisation de clé (GCM) | assertWithinSafetyBound() à 2^32 | Activable ; non imposé par défaut |
| Contournement des drapeaux de permission | Aucune — les drapeaux sont indicatifs | Un lecteur non conforme ignore les drapeaux |
| Divulgation de clé via les journaux | KeyMaterial rédaction ; #[\SensitiveParameter] | Un appelant qui journalise exposeKey() annule cette protection |
Comportement en mode FIPS
Section intitulée « Comportement en mode FIPS »Core n’est pas un module cryptographique validé FIPS et n’est pas certifié FIPS. CryptoCapabilities::detectFipsMode() est une sonde au mieux qui signale un état actif, absent ou indéterminé, et assertFipsAvailableForProfile() échoue en mode fermé lorsqu’un profil FIPS est sélectionné sur un hôte qui ne prouve pas la présence d’un fournisseur FIPS. La surface de chiffrement fonctionne dans un mode compatible FIPS quand elle s’exécute avec une build OpenSSL de l’hôte qui a chargé un fournisseur validé FIPS. Une posture validée et certifiée relève d’Enterprise.
Conformité
Section intitulée « Conformité »| Affirmation | Standard | Clause | Preuve |
|---|---|---|---|
| Chaque IV GCM est unique par invocation via une construction déterministe champ fixe plus compteur. | NIST SP 800-38D | §8.2.1 | |
| La discipline de construction d’IV empêche la réutilisation entre invocations sur une même clé. | NIST SP 800-38D | §8.1 | |
| Le plafond de texte en clair par objet correspond à la borne de longueur par invocation. | NIST SP 800-38D | §5.2.1.1 | |
| La cryptopériode de clé et la rotation sont une responsabilité du déploiement. | NIST SP 800-57 Partie 1 Rév. 5 | §4 | |
| La clé de fichier AES fait 256 bits, ce qui correspond à la longueur de clé du standard. | FIPS 197 | §4.2.1 | |
| La génération de clé résidente dans le jeton est le point d’intégration du magasin de clés externe. | OASIS PKCS#11 v3.1 | C_GenerateKey |
L’ISO 32000-2:2020 §7.6 et l’ISO/TS 32003:2023 §5.2 constituent la base normative des gestionnaires documentés ici. Leur texte est soumis à des restrictions de licence. Cette page les paraphrase et cite les clauses par leur numéro, sans en citer le texte. La preuve d’exécution vérifiée de la dérivation de clé exacte à l’octet près est le test de standards de l’algorithme 2.B et la fixture d’oracle externe listés dans le bloc de preuves en pied de page.
Contexte commercial
Section intitulée « Contexte commercial »Core fournit à la fois le chemin AES-256-CBC par défaut et le chemin AES-256-GCM activable, avec une surface à clé locale et le gate de la politique cryptographique. L’édition Enterprise ajoute un backend de garde de clé HSM/PKCS#11 et un profil de politique cryptographique en mode FIPS derrière les mêmes contrats. L’API publique est identique ; seuls le backend de garde de clé et l’implémentation de la politique diffèrent.
Voir aussi
Section intitulée « Voir aussi »- Sécurité — la vue d’ensemble du module de sécurité et la frontière des permissions.
- Contracts / Security Policy — le contrat de politique cryptographique qui contrôle le chiffre.
- Security / Signing — les signatures et les horodatages, la surface cryptographique voisine.
- Conformance — l’interdiction par PDF/A de la clé
Encrypt.