Cifrado: AES-256 (CBC) y AES-256-GCM
De un vistazo
Sección titulada «De un vistazo»El núcleo cifra un PDF con AES-256 mediante el controlador de seguridad estándar de la ISO 32000-2:2020 §7.6. El modo predeterminado es V=5 / R=6 / AESV3 (AES-256-CBC). Como modo opcional, ofrece la ruta autenticada AES-256-GCM V=6 / R=7 de la ISO/TS 32003:2023. Esta página documenta la derivación de clave, el formato de transmisión, el límite de permisos y las restricciones que todo despliegue debe comprender.
Instalación
Sección titulada «Instalación»composer require nextpdf/core:^3La ruta predeterminada requiere la extensión openssl. La ruta AES-256-GCM usa openssl o ext-sodium. En un host sin hardware AES-NI, libsodium rechaza GCM y el núcleo recurre a la implementación más lenta de OpenSSL, sin degradar el algoritmo.
Resumen conceptual
Sección titulada «Resumen conceptual»El valor predeterminado es el controlador de seguridad estándar V=5 / R=6 con el filtro de cifrado AESV3. Al invocar setEncryption(), el núcleo genera una clave de archivo aleatoria de 256 bits a partir de la fuente de aleatoriedad criptográfica de la plataforma (random_bytes()). Su longitud es de 32 bytes, lo que coincide con la longitud de clave de FIPS 197. El contenido por objeto se cifra con AES-256-CBC. El vector de inicialización de 16 bytes se antepone a cada texto cifrado, tal como indica la ISO 32000-2:2020 §7.6.4.
La derivación de clave sigue el algoritmo 2.B en la revisión 6. Primero, la contraseña se normaliza con SASLprep (RFC 4013) y luego se trunca a 127 bytes UTF-8 en un límite entre caracteres, tal como indica la ISO 32000-2:2020 §7.6.4.3.3. El hash derivado se calcula con una rutina iterativa de SHA-256 / SHA-384 / SHA-512 guiada por un paso AES-128-CBC, lo que eleva el costo de adivinar la contraseña fuera de línea. Las sales del usuario, del propietario y de cada clave se generan una vez por instancia del cifrador, de modo que una misma instancia emite bytes de diccionario deterministas, una condición previa para un escritor de varias pasadas.
useAesGcm() activa la ruta opcional AES-256-GCM. Implementa el filtro de cifrado AESV4 V=6 / R=7 de la ISO/TS 32003:2023. El cifrado es AES-256-GCM con parámetros de NIST SP 800-38D. En el formato de transmisión, cada objeto cifrado se dispone como un IV de 12 bytes, el texto cifrado y luego una etiqueta de autenticación de 16 bytes. Los datos autenticados adicionales están vacíos, tal como indica el perfil de la TS 32003 §5.2. El descifrado verifica la etiqueta y lanza TamperedDataException ante una discrepancia; nunca devuelve texto sin formato cuando la etiqueta no valida. Esta ruta añade la detección de modificaciones que la ruta CBC predeterminada no proporciona por sí sola.
La disciplina de unicidad de IV en la ruta GCM sigue NIST SP 800-38D §8. Los 4 bytes superiores del IV son un campo fijo por instancia establecido durante la construcción a partir de una fuente aleatoria. Los 8 bytes inferiores son un contador big-endian que se incrementa después de cada IV emitido. Esto coincide con el enfoque de construcción determinista de §8.2.1, salvo que el campo fijo se aleatoriza para impedir colisiones entre documentos en vez de enumerarse. Una segunda salvaguarda registra cada IV emitido en un conjunto de colisiones y lanza NonceReuseException si un valor se repite. El desbordamiento del contador también lanza NonceReuseException, porque constituye el modo de fallo por reutilización del IV contra el que advierte la §8.
En la ruta GCM se aplican dos límites de longitud. El límite máximo de texto sin formato por objeto es de 2^39 − 256 bytes, el límite por invocación derivado en NIST SP 800-38D §5.2.1.1. Una entrada mayor lanza una excepción de longitud con una indicación para particionarla entre objetos. El límite de seguridad de invocaciones es de 2^32 llamadas por clave. assertWithinSafetyBound() es una comprobación opcional que lanza GcmInvocationLimitExceededException para que quien la invoca rote la clave del documento antes del umbral de la §8.3. NIST SP 800-57 Parte 1 §4 plantea esta decisión sobre la vida útil de la clave como una responsabilidad del despliegue.
Los indicadores de permisos son orientativos. La máscara de bits se escribe en la entrada cifrada /Perms y en el valor /P, y se recupera con validatePerms() durante la lectura, que falla de forma segura ante un marcador corrupto. Se espera que un lector conforme respete los indicadores. Los indicadores no se imponen mediante criptografía: un procesador con la clave de descifrado que ignore los bits puede leer, copiar o modificar el contenido. Deben describirse los indicadores de permisos como una convención del lector, no como un control de acceso.
Superficie de la API
Sección titulada «Superficie de la API»| Tipo | Categoría | Miembros clave | Estabilidad | Desde |
|---|---|---|---|---|
Aes256Encryptor | clase | encrypt(), decrypt(), encryptForObject(), buildEncryptionDictionary(), verifyUserPassword(), verifyOwnerPassword(), validatePerms(), getEncryptionKey() | estable | 1.0.0 |
Aes256GcmEncryptor | clase | encrypt(), decrypt(), encryptStream(), assertWithinSafetyBound(), invocationCount(), isAvailable() | estable | 2.18.0 |
KeyMaterial | clase final de solo lectura | generate(), exposeKey(), fingerprint() | estable | 2.18.0 |
EncryptedPayloadSpec | clase final de solo lectura | toDict() | estable | 2.18.0 |
CryptoCapabilities | clase final | hasAesGcm(), detectFipsMode(), assertFipsAvailableForProfile() | estable | 2.0.0 |
NonceReuseException | excepción | — | estable | 2.18.0 |
TamperedDataException | excepción | — | estable | 2.18.0 |
DecryptionFailedException | excepción | — | estable | 2.18.0 |
GcmInvocationLimitExceededException | excepción | — | estable | 3.0.0 |
Ejemplo de código — Inicio rápido
Sección titulada «Ejemplo de código — Inicio rápido»<?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');Ejemplo de código — Producción
Sección titulada «Ejemplo de código — Producción»<?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; } }}El cifrador comprueba la capacidad del host, aplica la salvaguarda opcional de invocaciones, registra únicamente la huella no reversible de la clave y relanza el rechazo por manipulación en lugar de devolver bytes sospechosos.
Casos límite y trampas
Sección titulada «Casos límite y trampas»- La ruta predeterminada AES-256-CBC ofrece confidencialidad únicamente. No detecta por sí sola un texto cifrado modificado. Usar la ruta AES-256-GCM cuando se necesite detección de modificaciones.
useAesGcm()lanza una excepción cuando el modo PDF/A está activo y también cuando niopensslniext-sodiumofrecen AES-256-GCM. Capturar ambas y mostrar un mensaje sobre el que el operador pueda actuar.- En un host sin AES-NI, libsodium rechaza GCM. El núcleo recurre a OpenSSL GCM, que es correcto, aunque más lento; baja el rendimiento, no la seguridad.
- El límite máximo de texto sin formato por objeto en GCM es de
2^39 − 256bytes. Una entrada mayor lanza una excepción de longitud; particionar el contenido entre varios objetos conencryptStream(). - Una instancia de
KeyMaterialdebe tener exactamente 32 bytes; una longitud incorrecta se rechaza durante la construcción, no se trunca. - La ruta de lectura (
verifyUserPassword(),verifyOwnerPassword(),validatePerms()) usa comparación en tiempo constante sobre el material criptográfico y falla de forma segura ante un marcador de permisos corrupto.
Rendimiento
Sección titulada «Rendimiento»El cifrado AES-256-CBC por objeto es una sola llamada a OpenSSL, O(n) en el cuerpo del objeto. La derivación de clave ejecuta la rutina iterativa del algoritmo 2.B una vez por instancia del cifrador; el costo está acotado y es constante por documento. La ruta de streaming AES-256-GCM particiona la entrada en fragmentos de 16 MiB, lo que mantiene el montón activo acotado a aproximadamente 64 MB independientemente del tamaño total de la entrada, muy por debajo del presupuesto máximo documentado de 64 MB. Cada objeto GCM añade 28 bytes de sobrecarga (IV de 12 bytes más etiqueta de 16 bytes). El hardware AES-NI mejora notablemente el rendimiento de GCM; su ausencia solo reduce el rendimiento.
Notas de seguridad
Sección titulada «Notas de seguridad»El modelo de amenazas de esta superficie es explícito. El costo de adivinar la contraseña fuera de línea se eleva mediante la normalización SASLprep más la derivación de clave iterativa de la revisión 6, pero una contraseña débil sigue siendo el riesgo residual dominante. Ninguna derivación lo elimina. La modificación del texto cifrado se detecta en la ruta GCM mediante la verificación de la etiqueta y no se detecta en la ruta CBC predeterminada. La reutilización del IV en la ruta GCM se evita con un contador más un conjunto de colisiones, en consonancia con la disciplina de IV de NIST SP 800-38D §8.1. El desbordamiento del contador se rechaza en lugar de envolverse. La divulgación de la clave en los registros se mitiga con la ocultación de KeyMaterial y el atributo #[\SensitiveParameter] en las contraseñas. El material de clave derivado se pone a cero después de su uso allí donde la plataforma lo permite.
El límite es igual de explícito. El cifrado AES-256 se aplica según se define en la ISO 32000-2:2020 §7.6 y, para la ruta opcional, en la ISO/TS 32003:2023 §5.2; la protección efectiva depende de la fortaleza de la contraseña, la gestión de claves, el entorno de despliegue y el lector que consume el documento. Los indicadores de permisos los respetan los lectores conformes y no se imponen criptográficamente. El paso AES-ECB usado para el valor /Perms lo exige la ISO 32000-2:2020 §7.6.4.4.10 para un único bloque de 16 bytes. No es un modo de propósito general. La rotación de la clave antes del límite de 2^32 invocaciones es una responsabilidad del despliegue; el núcleo expone una comprobación para ello, pero no la impone de forma predeterminada.
Residencia de datos y mitigaciones de PII
Sección titulada «Residencia de datos y mitigaciones de PII»El cifrado y el descifrado se ejecutan en el proceso; ningún byte del documento, contraseña ni valor de clave abandona el host a través de esta superficie. El conjunto de colisiones de IV de GCM se indexa por una huella de clave no reversible, no por los bytes de la clave. Un despliegue que antepone a la clave un sistema externo de gestión de claves o un token PKCS#11 es responsable de la residencia de ese backend; OASIS PKCS#11 v3.1 C_GenerateKey es el punto de contrato para la generación de claves residente en el token.
Telemetría segura y depuración de registros
Sección titulada «Telemetría segura y depuración de registros»Registrar el nombre de la política y la huella de la clave de 8 caracteres, nunca la clave ni la contraseña. KeyMaterial::__toString() y __debugInfo() devuelven un marcador de posición oculto. Los mensajes de excepción de esta superficie incluyen una etiqueta de operación y una huella, no los bytes de la clave. El recuento de invocaciones de GCM es una señal de telemetría segura para los paneles de rotación de claves.
Modelo de amenazas
Sección titulada «Modelo de amenazas»| Amenaza | Mitigación en el núcleo | Límite residual |
|---|---|---|
| Adivinación de contraseña fuera de línea | SASLprep más derivación iterada de la revisión 6 | Una contraseña débil sigue siendo el riesgo dominante |
| Modificación del texto cifrado | Verificación de la etiqueta GCM (ruta opcional) | La ruta CBC ofrece confidencialidad únicamente |
| Reutilización del IV (GCM) | Campo fijo aleatorio más contador más conjunto de colisiones; el desbordamiento rechaza | — |
| Texto sin formato GCM demasiado largo | Comprobación de longitud en 2^39 − 256; orientación para particionar | Quien la invoca debe transmitir las entradas grandes mediante streaming |
| Uso excesivo de la clave (GCM) | assertWithinSafetyBound() en 2^32 | Opcional; no se impone de forma predeterminada |
| Elusión de los indicadores de permisos | Ninguna; los indicadores son orientativos | Un lector no conforme ignora los indicadores |
| Divulgación de la clave en los registros | KeyMaterial: ocultación; #[\SensitiveParameter] | Quien registra exposeKey() anula esta protección |
Comportamiento en modo FIPS
Sección titulada «Comportamiento en modo FIPS»El núcleo no es un módulo criptográfico validado por FIPS y no está certificado por FIPS. CryptoCapabilities::detectFipsMode() es una sonda de mejor esfuerzo que informa si está activo, ausente o indeterminado, y assertFipsAvailableForProfile() falla de forma segura cuando se selecciona un perfil FIPS en un host que no demuestra disponer de un proveedor FIPS. La superficie de cifrado opera en un modo compatible con FIPS cuando se ejecuta sobre una compilación de OpenSSL del host que haya cargado un proveedor validado por FIPS. Una postura validada y certificada queda en el ámbito de Enterprise.
Conformidad
Sección titulada «Conformidad»| Afirmación | Estándar | Cláusula | Evidencia |
|---|---|---|---|
| Cada IV de GCM es único por invocación mediante una construcción determinista de campo fijo más contador. | NIST SP 800-38D | §8.2.1 | |
| La disciplina de construcción del IV evita la reutilización entre invocaciones con una misma clave. | NIST SP 800-38D | §8.1 | |
| El tope de texto sin formato por objeto coincide con el límite de longitud por invocación. | NIST SP 800-38D | §5.2.1.1 | |
| El periodo criptográfico de la clave y su rotación son una responsabilidad del despliegue. | NIST SP 800-57 Parte 1 Rev. 5 | §4 | |
| La clave de archivo AES es de 256 bits, lo que coincide con la longitud de clave del estándar. | FIPS 197 | §4.2.1 | |
| La generación de claves residente en el token es el punto de integración con el almacén externo de claves. | OASIS PKCS#11 v3.1 | C_GenerateKey |
La ISO 32000-2:2020 §7.6 y la ISO/TS 32003:2023 §5.2 son la base normativa de los controladores documentados aquí. Su texto tiene restricciones de licencia. Esta página los parafrasea y cita la cláusula por número, y no reproduce ninguno de ellos. La evidencia de tiempo de ejecución verificada para la derivación byte a byte de la clave es la prueba de estándares del algoritmo 2.B, junto con el accesorio de oráculo externo enumerado en el pie de página de evidencia.
Contexto comercial
Sección titulada «Contexto comercial»El núcleo incluye tanto la ruta predeterminada AES-256-CBC como la ruta opcional AES-256-GCM, con una superficie de clave local y la compuerta de política criptográfica. La edición Enterprise añade un backend de custodia de claves HSM/PKCS#11 y un perfil de política criptográfica en modo FIPS detrás de los mismos contratos. La API pública es idéntica; difieren el backend de custodia de claves y la implementación de la política.
Véase también
Sección titulada «Véase también»- Seguridad — el resumen del módulo de seguridad y el límite de permisos.
- Contratos / Política de seguridad — el contrato de política criptográfica que controla el cifrado.
- Seguridad / Firma — firmas y marcas de tiempo, la superficie criptográfica relacionada.
- Conformidad — la prohibición, en PDF/A, de la clave
Encrypt.