Шифрование: AES-256 (CBC) и AES-256-GCM
Core шифрует файлы Portable Document Format (PDF) с помощью AES-256 (Advanced Encryption Standard с 256-битными ключами) в соответствии со стандартным обработчиком безопасности ISO 32000-2:2020 §7.6. Режим по умолчанию — V=5 / R=6 / AESV3 (AES-256-CBC, Cipher Block Chaining). Опциональный аутентифицированный режим — путь AES-256-GCM (Galois/Counter Mode) по ISO/TS 32003:2023 V=6 / R=7. Эта страница определяет вывод ключа, формат представления, границу разрешений и эксплуатационные ограничения.
Установка
Заголовок раздела «Установка»composer require nextpdf/core:^3Для пути по умолчанию требуется расширение openssl. Путь AES-256-GCM использует openssl или ext-sodium. На узлах без аппаратного AES-NI libsodium отказывается выполнять GCM; Core переключается на более медленную реализацию OpenSSL, не меняя алгоритм.
Концептуальный обзор
Заголовок раздела «Концептуальный обзор»По умолчанию используется стандартный обработчик безопасности V=5 / R=6 с крипто-фильтром AESV3. При вызове setEncryption() Core генерирует случайный 256-битный ключ файла с помощью криптографического источника случайности платформы (random_bytes()). Ключ имеет размер 32 байта, что соответствует длине ключа FIPS 197. Core шифрует содержимое каждого объекта с помощью AES-256-CBC. Перед каждым шифротекстом он добавляет 16-байтовый вектор инициализации, как предписывает ISO 32000-2:2020 §7.6.4.
Ключ выводится по алгоритму 2.B для ревизии 6. Core сначала нормализует пароль с помощью SASLprep (RFC 4013), затем усекает его до 127 байт UTF-8 на границе символа, как предписывает ISO 32000-2:2020 §7.6.4.3.3. Он вычисляет производный хеш по итеративной процедуре SHA-256 / SHA-384 / SHA-512 с шагом AES-128-CBC, что повышает стоимость офлайн-подбора пароля. Core генерирует соли пользователя, владельца и отдельную соль ключа один раз на экземпляр шифровщика, поэтому один экземпляр формирует детерминированные байты словаря — необходимое условие для многопроходной записи.
useAesGcm() включает опциональный путь AES-256-GCM. Он реализует крипто-фильтр AESV4 по ISO/TS 32003:2023 V=6 / R=7. Шифр — AES-256-GCM с параметрами из NIST SP 800-38D. Для каждого зашифрованного объекта формат представления такой: 12-байтовый IV, шифротекст и 16-байтовый тег аутентификации. Дополнительные аутентифицируемые данные пусты, как предписывает профиль TS 32003 §5.2. Расшифрование проверяет тег и при несовпадении выбрасывает TamperedDataException; после неудачной проверки тега оно никогда не возвращает открытый текст. Этот путь добавляет обнаружение изменений, которого путь CBC по умолчанию сам по себе не обеспечивает.
Путь GCM соблюдает правило уникальности IV из NIST SP 800-38D §8. Старшие 4 байта IV — фиксированное для экземпляра поле, задаваемое из случайного источника при конструировании. Младшие 8 байт — счётчик с порядком байтов big-endian, который увеличивается после каждого выданного IV. Это соответствует подходу детерминированного конструирования из §8.2.1, за исключением того, что фиксированное поле рандомизировано для предотвращения коллизий между документами, а не перечисляется. Дополнительная защита записывает каждый выданный IV в набор коллизий и выбрасывает NonceReuseException, если значение повторяется. Переполнение счётчика также выбрасывает NonceReuseException, поскольку оно ведёт к режиму отказа с повторным использованием IV, от которого предостерегает §8.
Для пути GCM действуют два ограничения длины. Предельный размер открытого текста на объект составляет 2^39 − 256 байт — ограничение на одно обращение из NIST SP 800-38D §5.2.1.1. Более крупный ввод выбрасывает исключение длины с рекомендацией распределить данные по объектам. Граница безопасности по числу обращений — 2^32 вызовов на ключ. assertWithinSafetyBound() — опциональная проверка: она выбрасывает GcmInvocationLimitExceededException, чтобы вызывающая сторона могла сменить ключ документа до порога §8.3. NIST SP 800-57 Part 1 §4 относит это решение о времени жизни ключа к ответственности развёртывания.
Флаги разрешений носят рекомендательный характер. Core записывает битовую маску в зашифрованную запись /Perms и значение /P, а при чтении восстанавливает её с помощью validatePerms(), так что повреждённый маркер приводит к безопасному отказу. Ожидается, что читатель, соответствующий стандарту, соблюдает эти флаги. Эти флаги не обеспечиваются криптографией: обработчик с ключом расшифрования, который игнорирует биты, может читать, копировать или изменять содержимое. Описывайте флаги разрешений как соглашение читателя, а не как контроль доступа.
Программный интерфейс
Заголовок раздела «Программный интерфейс»| Тип | Вид | Ключевые члены | Стабильность | С версии |
|---|---|---|---|---|
Aes256Encryptor | class | encrypt(), decrypt(), encryptForObject(), buildEncryptionDictionary(), verifyUserPassword(), verifyOwnerPassword(), validatePerms(), getEncryptionKey() | stable | 1.0.0 |
Aes256GcmEncryptor | class | encrypt(), decrypt(), encryptStream(), assertWithinSafetyBound(), invocationCount(), isAvailable() | stable | 2.18.0 |
KeyMaterial | final readonly class | generate(), exposeKey(), fingerprint() | stable | 2.18.0 |
EncryptedPayloadSpec | final readonly class | toDict() | stable | 2.18.0 |
CryptoCapabilities | final class | 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 |
Пример кода — Быстрый старт
Заголовок раздела «Пример кода — Быстрый старт»<?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');Пример кода — Продакшн
Заголовок раздела «Пример кода — Продакшн»<?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; } }}Шифр проверяет возможности узла, применяет опциональную защиту по числу обращений, логирует только необратимый отпечаток ключа и повторно пробрасывает исключения о подделке вместо возврата подозрительных байтов.
Граничные случаи и подводные камни
Заголовок раздела «Граничные случаи и подводные камни»- Путь AES-256-CBC по умолчанию обеспечивает только конфиденциальность. Сам по себе он не обнаруживает изменённый шифротекст. Используйте путь AES-256-GCM, когда нужно обнаруживать изменения.
useAesGcm()выбрасывает исключение, если активен режим PDF/A или если ниopenssl, ниext-sodiumне предоставляют AES-256-GCM. Перехватывайте оба случая и выводите сообщение, по которому оператор может принять меры.- На узлах без AES-NI libsodium отказывается выполнять GCM. Core переключается на OpenSSL GCM, что корректно, но медленнее; снижается пропускная способность, а не безопасность.
- Предельный размер открытого текста GCM на объект составляет
2^39 − 256байт. Более крупный ввод выбрасывает исключение длины; распределите содержимое по нескольким объектам с помощьюencryptStream(). - Экземпляр
KeyMaterialдолжен иметь размер ровно 32 байта. Конструктор отклоняет неверную длину, а не усекает её. - Путь чтения (
verifyUserPassword(),verifyOwnerPassword(),validatePerms()) использует сравнение с постоянным временем выполнения для криптографического материала и при повреждённом маркере разрешений безопасно отказывает.
Производительность
Заголовок раздела «Производительность»Шифрование AES-256-CBC одного объекта — это один вызов OpenSSL со сложностью O(n) по размеру тела объекта. При выводе ключа итеративная процедура алгоритма 2.B выполняется один раз на экземпляр шифровщика; стоимость ограничена и постоянна для документа. Потоковый путь AES-256-GCM разбивает ввод на блоки по 16 MiB, удерживая активное использование кучи примерно на уровне 64 MB независимо от общего размера ввода и оставаясь в пределах документированного пикового бюджета 64 MB. Каждый объект GCM добавляет 28 байт накладных расходов (12-байтовый IV плюс 16-байтовый тег). Аппаратный AES-NI существенно повышает пропускную способность GCM; без него снижается только пропускная способность.
Замечания по безопасности
Заголовок раздела «Замечания по безопасности»У этой поверхности шифрования явная модель угроз. Нормализация SASLprep вместе с итеративным выводом ключа ревизии 6 повышает стоимость офлайн-подбора пароля, но слабый пароль остаётся главным остаточным риском. Никакой алгоритм вывода ключа не устраняет этот риск. Путь GCM обнаруживает изменение шифротекста через проверку тега; путь CBC по умолчанию этого не делает. В пути GCM счётчик вместе с набором коллизий предотвращает повторное использование IV, что согласуется с правилом IV из NIST SP 800-38D §8.1. При переполнении счётчика происходит отказ, а не зацикливание. Маскирование KeyMaterial и атрибут #[\SensitiveParameter] на паролях снижают риск раскрытия ключа через логи. Производный ключевой материал обнуляется после использования там, где это позволяет платформа.
Граница тоже явно задана. Core применяет шифрование AES-256, как определено в ISO 32000-2:2020 §7.6, а для опционального пути — в ISO/TS 32003:2023 §5.2. Эффективная защита зависит от стойкости пароля, управления ключами, среды развёртывания и читателя, который обрабатывает документ. Читатели, соответствующие стандарту, соблюдают флаги разрешений, но криптография их не обеспечивает. Шаг AES-ECB, используемый для значения /Perms, предписан ISO 32000-2:2020 §7.6.4.4.10 для одного 16-байтового блока. Это не режим общего назначения. Core предоставляет проверку, позволяющую сменить ключ до достижения границы в 2^32 обращений, но по умолчанию её не обеспечивает; эта смена — ответственность развёртывания.
Резидентность данных и меры защиты PII
Заголовок раздела «Резидентность данных и меры защиты PII»Шифрование и расшифрование выполняются внутри процесса; ни байты документа, ни пароль, ни значение ключа не покидают узел через эту поверхность. Набор коллизий IV для GCM использует в качестве ключа необратимый отпечаток ключа, а не байты ключа. Если развёртывание размещает ключ во внешней системе управления ключами или токене PKCS#11, за резидентность отвечает этот бэкенд; OASIS PKCS#11 v3.1 C_GenerateKey — точка контракта для генерации ключа, размещаемого на токене.
Безопасная телеметрия и очистка логов
Заголовок раздела «Безопасная телеметрия и очистка логов»Логируйте имя политики и 8-символьный отпечаток ключа, но никогда — сам ключ или пароль. KeyMaterial::__toString() и __debugInfo() возвращают замаскированную заглушку. Исключения этой поверхности содержат метку операции и отпечаток, а не байты ключа. Счётчик обращений GCM — безопасная телеметрия для панелей мониторинга смены ключей.
Модель угроз
Заголовок раздела «Модель угроз»| Угроза | Меры в Core | Остаточная граница |
|---|---|---|
| Офлайн-подбор пароля | SASLprep плюс итеративный вывод ревизии 6 | Слабый пароль по-прежнему главный риск |
| Изменение шифротекста | Проверка тега GCM (опциональный путь) | Путь CBC обеспечивает только конфиденциальность |
| Повторное использование IV (GCM) | Случайное фиксированное поле плюс счётчик плюс набор коллизий; переполнение отказывает | — |
| Слишком длинный открытый текст GCM | Проверка длины на 2^39 − 256; рекомендация по разбиению | Вызывающая сторона должна передавать большой ввод потоком |
| Избыточное использование ключа (GCM) | assertWithinSafetyBound() на 2^32 | Опционально; по умолчанию не обеспечивается |
| Обход флагов разрешений | Нет — флаги носят рекомендательный характер | Несоответствующий стандарту читатель игнорирует эти флаги |
| Раскрытие ключа через логи | Маскирование KeyMaterial; #[\SensitiveParameter] | Вызывающая сторона, логирующая exposeKey(), сводит это на нет |
Поведение в режиме FIPS
Заголовок раздела «Поведение в режиме FIPS»Core не является криптографическим модулем, валидированным по FIPS, и не сертифицирован по FIPS. CryptoCapabilities::detectFipsMode() — это проба по принципу наилучших усилий, которая сообщает “активен”, “отсутствует” или “не определено”. assertFipsAvailableForProfile() безопасно отказывает, когда профиль FIPS выбран на узле, который не подтверждает наличие провайдера FIPS. Поверхность шифрования работает в FIPS-совместимом режиме, когда выполняется на сборке OpenSSL узла, загрузившей провайдер, прошедший валидацию FIPS. Валидированная, сертифицированная конфигурация — задача Enterprise.
Соответствие
Заголовок раздела «Соответствие»| Утверждение | Стандарт | Пункт | Свидетельство |
|---|---|---|---|
| Каждый IV для GCM уникален для каждого обращения благодаря детерминированному конструированию “фиксированное поле плюс счётчик”. | NIST SP 800-38D | §8.2.1 | |
| Правило конструирования IV предотвращает повторное использование между обращениями на одном ключе. | NIST SP 800-38D | §8.1 | |
| Предельный размер открытого текста на объект соответствует ограничению длины на одно обращение. | NIST SP 800-38D | §5.2.1.1 | |
| Криптопериод ключа и его смена — ответственность развёртывания. | NIST SP 800-57 Часть 1, ред. 5 | §4 | |
| Ключ файла AES имеет размер 256 бит, что соответствует длине ключа стандарта. | FIPS 197 | §4.2.1 | |
| Генерация ключа, размещаемого на токене, — точка интеграции с внешним хранилищем ключей. | OASIS PKCS#11 v3.1 | C_GenerateKey |
ISO 32000-2:2020 §7.6 и ISO/TS 32003:2023 §5.2 — нормативная основа для обработчиков, описанных здесь. Их текст ограничен лицензией. Эта страница пересказывает эти стандарты, ссылается на пункты по номерам и не цитирует ни один из них. Стандартный тест алгоритма 2.B и фикстура внешнего оракула в трейлере свидетельств страницы дают проверяемое во время выполнения свидетельство побайтово точного вывода ключа.
Коммерческий контекст
Заголовок раздела «Коммерческий контекст»Core поставляет путь AES-256-CBC по умолчанию, опциональный путь AES-256-GCM, поверхность локальных ключей и шлюз криптополитики. Издание Enterprise добавляет бэкенд хранения ключей HSM/PKCS#11 и профиль криптополитики режима FIPS за теми же контрактами. Публичный API идентичен; различаются бэкенд хранения ключей и реализация политики.
См. также
Заголовок раздела «См. также»- Security — обзор модуля безопасности и граница разрешений.
- Contracts / Security Policy — контракт криптополитики, который управляет доступом к шифру.
- Security / Signing — подписи и метки времени, родственная криптографическая поверхность.
- Conformance — запрет PDF/A на ключ
Encrypt.