Перейти к содержимому
getnextpdf.com

Enterprise редакция

Подписание через аппаратный модуль безопасности (PKCS#11)

NextPDF Enterprise подписывает PDF ключом, хранящимся внутри аппаратного модуля безопасности (HSM). Вы указываете подписанту токен PKCS#11 — смарт-карту, токен Universal Serial Bus (USB) или сетевой HSM — и операция подписания выполняется на устройстве. Закрытый ключ никогда не покидает границу токена. Эта страница описывает поведение: что делает подписант, что предоставляете вы и где хранение ключей перестаёт быть ответственностью NextPDF.

Подписант HSM разрешается через контракт подписанта Core, поэтому ваше приложение зависит от контракта, а не от конкретного типа Enterprise. Он расширяет тот же путь подписания Cryptographic Message Syntax (CMS), что использует Core, за исключением того, что криптографическая операция делегируется токену.

Предварительные требования указаны во фронтматтере и повторены в разделе Предварительные требования, чтобы вы не были застигнуты врасплох посреди задачи.

Эта возможность поставляется в NextPDF Enterprise (nextpdf/enterprise) и активируется лицензионным конвертом уровня Enterprise. Развёртывание без этого права не загружает классы возможности. Сравнить редакции и получить лицензию.

NextPDF Core поставляет программный подписант CMS, который держит ключ в процессе или принимает его через контракт стратегии подписания Core; NextPDF Pro добавляет удалённые и облачные стратегии подписания key-management-service (KMS). Аппаратное хранение ключей через PKCS#11 — это возможность Enterprise, не предоставляемая Core или Pro.

Токен PKCS#11 предоставляет криптографические объекты — сертификаты и закрытые ключи — за разделяемой библиотекой производителя. Подписант Enterprise адаптирует эту библиотеку:

  1. Он открывает разделяемую библиотеку токена один раз на процесс и кэширует дескриптор модуля, потому что PKCS#11 требует, чтобы модуль был инициализирован ровно один раз на процесс.
  2. Он открывает сессию на настроенном слоте и входит в систему с предоставленным PIN. Вход аутентифицирует пользователя до любой операции с закрытым ключом, согласно PKCS#11 v3.1 §5.6.8.
  3. Он находит сертификат подписания на токене по метке, читает сертификат в форме Distinguished Encoding Rules (DER) и определяет алгоритм открытого ключа.
  4. Во время подписания он находит закрытый ключ по метке — которая на некоторых токенах может отличаться от метки сертификата — и просит токен вычислить подпись. Данные для подписи передаются внутрь; ключ остаётся на устройстве.

Подписант поддерживает RSA с дополнением PKCS#1 v1.5 (SHA-256, SHA-384, SHA-512), RSA с дополнением Probabilistic Signature Scheme (PSS), где длина соли равна длине дайджеста, и Elliptic Curve Digital Signature Algorithm (ECDSA) с SHA-256, SHA-384 и SHA-512. Кривая ECDSA и дайджест сопоставляются по соглашению — P-256 с SHA-256, P-384 с SHA-384, P-521 с SHA-512 — следуя рекомендуемому сопоставлению в RFC 5480. Токен возвращает подпись ECDSA как сырую конкатенацию двух целых чисел; подписант преобразует её в форму с кодировкой DER, которую ожидают PDF и OpenSSL.

Для генерации подписи ключ RSA не менее 2048 бит и порядок кривой ECDSA не менее 224 бит — это приемлемые минимумы согласно NIST SP 800-131A Rev.2 §3. Подготавливайте ключ токена с такими размерами или выше.

Существует альтернативный путь через движок OpenSSL для токенов с поддержкой движка. В OpenSSL 3.x расширение OpenSSL для PHP не предоставляет интерфейс прикладного программирования (API) движка, поэтому класс движка устарел; поддерживаемый маршрут с движком запускает командно-строчный бинарник OpenSSL. Предпочитайте прямой путь PKCS#11 там, где у вашего токена есть библиотека PKCS#11.

Несущее решение состоит в том, что закрытый ключ никогда не покидает токен. Поэтому подписант делегирует криптографическую операцию устройству и переносит через шов PKCS#11 только данные для подписи. Он никогда не читает и не восстанавливает ключевой материал в памяти PHP. Он разрешается через контракт Core HsmSignerInterface, а не через конкретный тип Enterprise, поэтому код подписания одинаков независимо от того, живёт ли ключ в программном обеспечении, в облачном KMS или в аппаратном токене. Он кэширует дескриптор модуля один раз на процесс, потому что PKCS#11 инициализирует каждый модуль ровно один раз на процесс, а затем преобразует сырой вывод ECDSA токена в DER, чтобы валидаторы видели ожидаемую кодировку. Форму определяет хранение, а не удобство: граница доверия остаётся на краю устройства.

Фоновая информация о проектировании: Подписание с опорой на HSM.

Прежде чем подписывать с HSM, подтвердите каждый пункт:

  1. Установите NextPDF Core и пакет Enterprise: composer require nextpdf/core:^3 и composer require nextpdf/enterprise.
  2. Держите активную лицензию NextPDF Enterprise; разрешайте пакет относительно учётных данных вашей лицензии на Private Packagist.
  3. Установите разделяемую библиотеку PKCS#11 производителя токена на хост (например, .so в Linux или .dll в Windows) и запишите её абсолютный путь, номер слота и метки объектов.
  4. Загрузите расширение PHP ext-pkcs11. Оно не входит в стандартный PHP и должно устанавливаться отдельно. Конструктор подписанта вызывает типизированную ошибку операции, когда расширение отсутствует.

Предоставьте подписанту эти входные данные:

  • Путь к библиотеке — абсолютный путь к разделяемой библиотеке PKCS#11 производителя.
  • Идентификатор слота — номер слота токена, обычно 0.
  • PIN — PIN токена. Считайте его секретом: предоставляйте из менеджера секретов, а никогда из исходного кода или журналов. Подписант помечает параметр PIN конфиденциальным, поэтому он исключён из трассировок стека и сериализации.
  • Метка сертификата — метка объекта сертификата на токене.
  • Метка ключа — метка объекта закрытого ключа, когда она отличается от метки сертификата.
  • Цепочка — опциональные промежуточные сертификаты в форме DER, когда токен их не хранит.

Проверяйте доступность токена до конструирования подписанта. Конструирование читает сертификат с токена, поэтому неправильно настроенный слот или метка дают быстрый сбой с типизированной ошибкой, а не во время подписания.

  1. Подтвердите, что среда выполнения поддерживает PKCS#11, проверив доступность расширения. Не конструируйте подписанта, когда расширение отсутствует.
  2. Прочитайте PIN из менеджера секретов в переменную, которая никогда не логируется.
  3. Сконструируйте подписанта HSM с путём к библиотеке, слотом, PIN и метками. Конструирование входит в систему и читает сертификат.
  4. Передайте подписанта оркестратору подписания Core через HsmSignerInterface. Оркестратор вычисляет диапазон байтов, строит подписанные атрибуты CMS, передаёт данные токену и собирает подписанный PDF.
  5. Перехватите наиболее конкретный сбой, залогируйте структурное сообщение без PIN и перевыбросите исключение.
examples/contracts/hsm-signer-availability.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\HsmSignerInterface;
/**
* Build a hardware-token signer only when the runtime supports it.
*
* The concrete PKCS#11 signer is resolved through the Core contract so the
* caller depends on the interface, not the Enterprise implementation type.
* The PIN arrives from a secret resolver; it is never written to source.
*
* @param callable(): bool $pkcs11Available Reports ext-pkcs11 availability.
* @param callable(): HsmSignerInterface $signerFactory Builds the configured token signer.
*
* @throws \RuntimeException When the PKCS#11 extension is not loaded.
*
* @return HsmSignerInterface The token signer, ready for the Core orchestrator.
*/
function resolveHsmSigner(callable $pkcs11Available, callable $signerFactory): HsmSignerInterface
{
if ($pkcs11Available() !== true) {
throw new \RuntimeException(
'PKCS#11 signing requires the ext-pkcs11 extension; install it before signing.',
);
}
return $signerFactory();
}

Продакшн-связка — точный список аргументов конструктора и типы типизированных исключений — описана в подробном справочнике по HSM.

examples/contracts/hsm-sign-guarded.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\HsmSignerInterface;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final readonly class HsmSigningService
{
public function __construct(
private HsmSignerInterface $signer,
private LoggerInterface $logger,
) {}
/**
* Sign data on the token through the Core HSM contract.
*
* The byte range is computed by the engine, never accepted from the
* caller. The token performs the signing operation; the private key
* does not leave the device.
*
* @param string $data The bytes the orchestrator hands to the token.
* @param string $algorithm The OpenSSL-style signing algorithm identifier.
*
* @throws NextPdfException When the token operation fails.
*
* @return string The raw signature bytes returned by the token.
*/
public function sign(string $data, string $algorithm): string
{
try {
return $this->signer->sign($data, $algorithm);
} catch (NextPdfException $e) {
// Structural message only — never the PIN or key material.
$this->logger->error('HSM signing failed', ['reason' => $e->getMessage()]);
throw $e;
}
}
}

Подтвердите результат так, как это сделал бы верификатор:

  1. Считайте обратно сертификат подписанта и цепочку в форме DER от подписанта и убедитесь, что они совпадают с сертификатом, подготовленным на токене.
  2. Откройте подписанный PDF в валидаторе, настроенном с вашими якорями доверия, и убедитесь, что подпись сообщается как криптографически целостная. Созданная подпись — это не проверенная подпись; решение о доверии принадлежит верификатору и его якорям доверия, а не производителю.
  3. Для подписи ECDSA убедитесь, что встроенная подпись имеет кодировку DER — подписант преобразует сырой вывод токена за вас, поэтому валидатор, отклоняющий форму сырой конкатенации, всё равно должен принять встроенную подпись.
  4. Убедитесь, что в журналах вашего приложения не появляется ни PIN, ни метка токена, ни ключевой материал.
  • Ключ остаётся на токене. Данные для подписи передаются токену; операция подписания выполняется внутри границы токена. Закрытый ключ никогда не загружается в память PHP.
  • PIN — это секрет. Это конфиденциальный параметр конструктора, исключённый из журналов и сериализации. Предоставляйте его из менеджера секретов. Повторная неудачная повторная аутентификация может заблокировать PIN на токене; политику обеспечивает токен, а не NextPDF.
  • Fail-closed. Ошибка токена или HSM вызывает типизированное исключение. Подписант не создаёт неподписанный или частично подписанный результат и никогда не подставляет более слабый алгоритм.
  • Стойкость алгоритма. Подготавливайте ключи RSA не менее 2048 бит и кривые ECDSA с порядком не менее 224 бит — приемлемые минимумы для генерации подписи согласно NIST SP 800-131A Rev.2 §3.
  • Постквантовое подписание экспериментально и по умолчанию выключено. Постквантовый путь существует за явным флагом включения. Стандартные долгосрочные архивные профили PDF Advanced Electronic Signatures (PAdES) пока не распознают постквантовые наборы, и большинство программ просмотра отклоняют их при проверке. Не включайте его для продакшн-подписей PAdES.

Эта страница касается криптографического подписания и интеграции с аппаратным модулем безопасности. Каждый нормативный источник приведён в пересказе; нормативный текст не воспроизводится. ### Граница хранения ключей

NextPDF Enterprise интегрируется с токеном PKCS#11 или HSM. Он не хранит, не генерирует и не гарантирует безопасность ключа подписания. Безопасность ключа зависит от токена или HSM, от развёртывания и от оператора — а не только от NextPDF Enterprise. Вы отвечаете за подготовку токена, обработку PIN, настройку слота и сетевую защиту сетевого HSM.

  • Расширение отсутствует. Конструирование подписанта PKCS#11 вызывает типизированное исключение операции, когда ext-pkcs11 не загружено. Сначала проверьте доступность.
  • Сертификат или ключ не найден по метке. Конструирование или подписание вызывает типизированное исключение, называющее недостающий объект. Подтвердите метку и слот.
  • Уже выполнен вход. Когда несколько экземпляров подписанта разделяют кэшированный модуль для одного слота, подписант выходит из системы и входит снова, чтобы обеспечить свежую проверку PIN — это требуется токенам персональной идентификации с политикой «PIN каждый раз».
  • Неподдерживаемый алгоритм. Запрос алгоритма, который подписант не сопоставляет, вызывает ошибку аргумента, а не подписание с подстановкой.
  • Сетевой HSM недостижим. Сетевая ошибка или ошибка устройства вызывает типизированное исключение; подписант никогда тихо не создаёт неподписанный документ.

Эта страница документирует только внешне наблюдаемое поведение и поддерживаемую публичную поверхность API. Внутренние пути пространств имён, вспомогательные классы, таблицы механизмов, имена файлов runbook и префиксы тикетов находятся вне области рассмотрения.