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

Pro редакция

Облачная подпись через KMS — глубокий справочник

Эта страница — справочник контрактного уровня по поверхности облачной KMS-подписи NextPDF Pro. Поверхность состоит из одного Service Provider Interface, NextPDF\Pro\Security\Signing\Kms\KmsSignerInterface, и трёх подписантов провайдеров: AwsKmsSigner, AzureKeyVaultSigner и GcpKmsSigner. Два адаптера, AwsKmsSigningStrategy и AzureKeyVaultSigningStrategy, связывают подписанта с контрактом Pro SigningStrategy. Каждый подписант отправляет своему провайдеру только дайджест сообщения по HTTP через PSR-18. Приватный ключ и документ не пересекают границу. Эта страница описывает публичный API, контракт наблюдаемого поведения и типизированные режимы отказа. Оркестрация сессий (RemoteSigningSession, SequentialSigner) и метки времени (PadesBtTimestamper) описаны на собственных страницах.

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

СимволПараметрыПоведение по умолчаниюВозвращаетБросает или отказывает сПримечания
KmsSignerInterfaceРасширяет контракт Core HsmSignerInterfaceSPI для драйверов KMS и HSM; зарезервированные встроенные идентификаторы: aws-kms, azure-keyvault, gcp-kms, pkcs11, openssl-cli
KmsSignerInterface::providerId()нетСтабильный ключ поиска в реестреnon-empty-stringСторонние драйверы должны выделять свой идентификатор в пространство имён
KmsSignerInterface::signWithVersion()$data, $algorithm = 'sha256WithRSAEncryption', $keyVersion = nullnull-версия ключа откатывается к умолчанию провайдераstring октеты подписи: RSA как возвращено провайдером (помещаются напрямую в SignerInfo.signature), ECDSA как DER ECDSA-Sig-Value по правилам CMSKeyManagementException, UnsupportedAlgorithmException, SignatureFailedExceptionСемантика null различается у провайдеров; см. контракт поведения
KmsSignerInterface::supportsAlgorithm()string $algorithmПроба возможностей; ввода-вывода не выполняетboolВызывается перед выбором провайдера
KmsSignerInterface::supportedAlgorithms()нетПеречисляет имена в стиле OpenSSL, принимаемые провайдеромlist<non-empty-string>
AwsKmsSignerконструктор: AwsKmsConfig, DER сертификата, DER цепочки, клиент PSR-18, фабрики PSR-17, логгер PSR-3Алгоритм по умолчанию KmsSigningAlgorithm::RsaPkcs1Sha256см. методыfinal; PROVIDER_ID = 'aws-kms'
AwsKmsSigner::create()id ключа, DER сертификата, зависимости PSR, опциональная цепочка, конфиг, логгерСтроит AwsKmsConfig::fromEnvironment($keyId), когда $config равно nullselfЧитает стандартные переменные окружения AWS_*
AwsKmsSigner::withAlgorithm()KmsSigningAlgorithm $algorithmВозвращает изменённый клонselfДолжен соответствовать типу ключа, подготовленного в AWS KMS
AwsKmsSigner::sign()$data, $algorithm = 'sha256WithRSAEncryption'Делегирует в signWithVersion($data, $algorithm, null)stringкак signWithVersion()Путь устаревшего двухаргументного контракта Core
AzureKeyVaultSignerконструктор: AzureKeyVaultConfig, DER сертификата, DER цепочки, клиент PSR-18, фабрики PSR-17, логгер PSR-3Алгоритм по умолчанию AzureSigningAlgorithm::Rs256; access-токен из конфига задаёт bearer-токенсм. методыfinal; PROVIDER_ID = 'azure-keyvault'
AzureKeyVaultSigner::create()имя хранилища, имя ключа, DER сертификата, зависимости PSR, опциональная цепочка, конфиг, логгерСтроит AzureKeyVaultConfig::fromEnvironment(), когда $config равно nullselfПоддерживает заранее полученный токен или учётные данные service principal
AzureKeyVaultSigner::withAlgorithm()AzureSigningAlgorithm $algorithmВозвращает изменённый клонselfКлючи RSA используют значения RS/PS; ключи EC — значения ES
GcpKmsSignerконструктор: GcpKmsConfig, DER сертификата, DER цепочки, клиент PSR-18, фабрики PSR-17, логгер PSR-3Алгоритм по умолчанию GcpKmsSigningAlgorithm::RsaSignPkcs1_2048Sha256см. методыfinal; PROVIDER_ID = 'gcp-kms', API_VERSION = 'v1'
GcpKmsSigner::create()id проекта, локация, кольцо ключей, крипто-ключ, DER сертификата, зависимости PSR, опциональная цепочка, конфиг, логгерСтроит GcpKmsConfig::fromEnvironment(), когда $config равно nullselfПолучение bearer-токена делегируется вызывающей стороне
GcpKmsSigner::withAlgorithm()GcpKmsSigningAlgorithm $algorithmТолько предпросмотр на этапе конфигурации; на этапе подписи побеждает имя wire каждого вызоваselfРазмер ключа зафиксирован подготовленной CryptoKeyVersion
AwsKmsSigningStrategyконструктор: AwsKmsSigner $signerСинхронный; isAsync() возвращает falseПробрасывает исключения обёрнутого подписантаАдаптер для RemoteSigningSession::complete()
AzureKeyVaultSigningStrategyконструктор: AzureKeyVaultSigner $signerСинхронный; isAsync() возвращает falseПробрасывает исключения обёрнутого подписантаАдаптер для RemoteSigningSession::complete()
KmsSigningAlgorithmenum, 9 вариантов (RSA PKCS#1, RSA-PSS, ECDSA; SHA-256/384/512)Значения wire SigningAlgorithm AWS KMSInvalidArgumentException из fromOpenSslName()resolveForWireName() сохраняет настроенный дайджест PSS
AzureSigningAlgorithmenum, 9 вариантов (RS256ES512)Значения в стиле JWA Azure Key VaultInvalidArgumentException из fromOpenSslName()isEcdsa() помечает значения, вывод которых требует конверсии в DER
GcpKmsSigningAlgorithmenum, 10 вариантов (EC P-256/P-384, RSA PKCS#1, RSA-PSS)Значения алгоритма CryptoKeyVersion GCPUnsupportedAlgorithmException из fromOpenSslName()Разрешение имени wire выбирает наименьший подходящий размер ключа
public function providerId(): string;
public function signWithVersion(
string $data,
string $algorithm = 'sha256WithRSAEncryption',
?string $keyVersion = null,
): string;
public function supportsAlgorithm(string $algorithm): bool;
public function supportedAlgorithms(): array;
public static function create(
string $keyId,
string $certDer,
ClientInterface $httpClient,
RequestFactoryInterface $requestFactory,
StreamFactoryInterface $streamFactory,
array $chainDer = [],
?AwsKmsConfig $config = null,
?LoggerInterface $logger = null,
): self
public function withAlgorithm(KmsSigningAlgorithm $algorithm): self
public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): string
public static function create(
string $vaultName,
string $keyName,
string $certDer,
ClientInterface $httpClient,
RequestFactoryInterface $requestFactory,
StreamFactoryInterface $streamFactory,
array $chainDer = [],
?AzureKeyVaultConfig $config = null,
?LoggerInterface $logger = null,
): self
public function withAlgorithm(AzureSigningAlgorithm $algorithm): self
public static function create(
string $projectId,
string $location,
string $keyRing,
string $cryptoKey,
string $certDer,
ClientInterface $httpClient,
RequestFactoryInterface $requestFactory,
StreamFactoryInterface $streamFactory,
array $chainDer = [],
?GcpKmsConfig $config = null,
?LoggerInterface $logger = null,
): self
public function withAlgorithm(GcpKmsSigningAlgorithm $algorithm): self
public function __construct(
private AwsKmsSigner $signer,
) {}
public function sign(string $signedAttributesDer): string
public function __construct(
private AzureKeyVaultSigner $signer,
) {}
public function sign(string $signedAttributesDer): string

KmsSignerInterface расширяет контракт Core HsmSignerInterface. Он добавляет providerId(), учитывающий версию ключа signWithVersion(), а также пробы возможностей supportsAlgorithm() и supportedAlgorithms(). Унаследованный двухаргументный sign() делегирует в signWithVersion() с null-версией ключа во всех трёх подписантах. getCertificateDer(), getCertificateChainDer() и getPublicKeyAlgorithm() реализованы на основе материала, переданного в конструктор. Пробы возможностей не выполняют ввода-вывода. Каждый подписант также предоставляет аксессоры getSigningAlgorithm() и getConfig() для инспекции.

Каждый подписант хеширует $data локально дайджестом разрешённого алгоритма и передаёт только этот дайджест. AWS получает base64-дайджест с MessageType: DIGEST. Azure получает base64url-дайджест в теле запроса подписи. GCP получает base64-дайджест в поле дайджеста, специфичном для алгоритма. Байты документа никогда не появляются в запросе к провайдеру. Весь транспорт использует стандартный HTTP-клиент PSR-18 поверх HTTPS-эндпоинта провайдера; никакой облачный вендорский SDK не задействован.

signWithVersion() проверяет аргумент версии ключа по принципу fail-closed до построения любого запроса. Значение, не проходящее грамматику провайдера, поднимает KeyManagementException и предотвращает инъекцию сегмента URL или KeyId.

Провайдерnull-версия ключаПустая строкаГрамматика переопределения
AwsKmsSignerИспользует AwsKmsConfig::$keyId; алиас или ARN разрешается в текущий ключ на стороне провайдераОтклоняетсяUUID (с дефисами или без), alias/<name> или ARN ключа/алиаса KMS
AzureKeyVaultSignerИспользует настроенную версию ключа; пустое значение в конфиге выбирает последнюю включённую версию на стороне сервераОтклоняется32-символьный шестнадцатеричный идентификатор
GcpKmsSignerИспользует версию, привязанную в GcpKmsConfig; если не привязана — поднимает KeyManagementExceptionОтклоняетсяДесятичный id CryptoKeyVersion, только цифры

У GCP нет серверного примитива «активной версии». Эндпоинт асимметричной подписи работает только над конкретным ресурсом cryptoKeyVersions/{n}, поэтому версия всегда должна быть разрешимой.

Слой стратегии передаёт имя wire в стиле OpenSSL. AWS и Azure принимают семь имён wire (PKCS#1 и ECDSA при SHA-256/384/512, плюс RSASSA-PSS). GCP принимает пять (sha256WithRSAEncryption, sha512WithRSAEncryption, RSASSA-PSS, ecdsa-with-SHA256, ecdsa-with-SHA384). Имя wire RSASSA-PSS не кодирует дайджест, поэтому оно неоднозначно по дайджесту. AwsKmsSigner разрешает его через KmsSigningAlgorithm::resolveForWireName(), который сохраняет дайджест настроенного варианта PSS. AzureKeyVaultSigner доверяет настроенному варианту PSS для неоднозначного имени. Он поднимает UnsupportedAlgorithmException, если разрешённый дайджест PSS расходится с настроенным. GcpKmsSigner заново разрешает enum из имени wire на каждом вызове; withAlgorithm() на GCP — это предпросмотр на этапе конфигурации и не меняет поведение на этапе подписи. Неподдерживаемое имя wire поднимает UnsupportedAlgorithmException до любого сетевого вызова. На AwsKmsSigner и GcpKmsSigner вызов подписи обновляет значение, впоследствии сообщаемое getSigningAlgorithm(), до разрешённого на этот вызов алгоритма. На AzureKeyVaultSigner разрешение локально для вызова, и настроенное значение остаётся авторитетным.

AWS и GCP возвращают подписи в форме, которую потребляет CMS: октеты подписи RSA попадают в SignerInfo.signature без изменений, а ECDSA приходит в DER-кодировке. Azure возвращает ECDSA в сыром виде IEEE P1363 (r||s), который подписант конвертирует в DER ECDSA-Sig-Value перед возвратом.

Адаптер SigningStrategy подписывает DER-кодированные подписанные атрибуты, предоставленные сессией. При наличии подписанных атрибутов вход подписи CMS — это дайджест полной DER-кодировки значения SignedAttrs — RFC 5652 §5.4. Методы адаптера getSignatureAlgorithmOid() и getDigestAlgorithm() питают поля SignerInfo signatureAlgorithm и digestAlgorithm — RFC 5652 §5.3. Возвращённые байты становятся OCTET STRING подписи SignerInfo — RFC 5652 §5.5. Сборка CMS, обработка ByteRange и жизненный цикл сессии принадлежат RemoteSigningSession; многосторонние потоки принадлежат SequentialSigner. Метка времени подписи PAdES B-T, чей messageImprint хеширует значение подписи SignerInfo — RFC 3161 Appendix A — применяется PadesBtTimestamper, а не этими подписантами. Все три описаны в глубоком справочнике безопасности Pro.

  • Версия ключа в виде пустой строки отклоняется у всех трёх провайдеров. Передайте null, чтобы унаследовать настроенное умолчание.
  • Некорректная версия ключа отклоняется до построения любого запроса, а нарушающее значение указывается в исключении.
  • AwsKmsSigner с пустым AwsKmsConfig::$keyId и null-версией ключа поднимает KeyManagementException.
  • Ответы провайдера, указывающие на сбой управления ключами, отображаются в KeyManagementException: AWS NotFoundException, DisabledException, KeyUnavailableException, InvalidKeyUsageException или HTTP 404; Azure HTTP 404, KeyNotFound, KeyDisabled или KeyNotActive; GCP HTTP 404 или 409, NOT_FOUND, FAILED_PRECONDITION либо HTTP 400, сообщение которого называет версию.
  • Прочие ответы провайдера, отличные от 200, поднимают SignatureFailedException на AWS и GCP и AzureKeyVaultException на Azure.
  • Сбой транспорта PSR-18 во время подписи отображается в SignatureFailedException с сохранением клиентского исключения как предыдущего throwable.
  • AzureKeyVaultSigner без access-токена и без учётных данных service principal поднимает AzureKeyVaultException до любого обращения к хранилищу. Неудачное получение токена Azure AD также поднимает AzureKeyVaultException.
  • AzureKeyVaultSigner проверяет имя хранилища, имя ключа, версию ключа и tenant id по опубликованным грамматикам Azure в узловой точке запроса. Значение, несущее URL-структурные символы, отказывает по принципу fail-closed с AzureKeyVaultException.
  • GcpKmsSigner без OAuth2 bearer-токена поднимает SignatureFailedException; получение токена — ответственность вызывающей стороны.
  • Ответ провайдера, не являющийся валидным JSON или не содержащий поля подписи, поднимает SignatureFailedException (Azure: отсутствие поля value поднимает AzureKeyVaultException).
  • Поле подписи провайдера, не проходящее base64-декодирование, поднимает SignatureFailedException на AWS и GCP и AzureKeyVaultException на Azure.
  • В версии 3.1.0 не поставляется адаптер SigningStrategy для GcpKmsSigner. Подписант GCP потребляется напрямую через контракт KmsSignerInterface.

AwsKmsConfig::withFipsEndpoint() направляет запросы на эндпоинт kms-fips региона. Статус FIPS-валидации этого эндпоинта — свойство AWS, а не NextPDF. AzureKeyVaultConfig и GcpKmsConfig не предоставляют выделенного помощника для FIPS-эндпоинта в 3.1.0. Вычисление дайджеста выполняется в процессе функцией PHP hash() и само по себе не является валидированным модулем. NextPDF Pro может работать против границы KMS или HSM, прошедшей FIPS-валидацию, но NextPDF не является FIPS-валидированным криптографическим модулем и не заявляет FIPS-сертификации.

ЗаявлениеСтандартПункт
Стратегия подписывает DER-кодированные подписанные атрибуты; дайджест входа подписи CMS покрывает полную DER-кодировку SignedAttrs.RFC 5652§5.4
SignedAttributes DER-кодированы и несут как минимум content-type и message-digest; signatureAlgorithm идентифицирует алгоритм подписанта.RFC 5652§5.3
Возвращённые байты подписи закодированы как OCTET STRING и помещены в поле подписи SignerInfo.RFC 5652§5.5
messageImprint метки времени подписи хеширует значение подписи SignerInfo (смежная поверхность B-T, не эти подписанты).RFC 3161Appendix A

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

  • Доступность внутри пакета Pro: AwsKmsSigner с 1.9.0, AzureKeyVaultSigner с 2.0.0, GcpKmsSigner и KmsSignerInterface с 2.1.0. Все актуальны в nextpdf/pro 3.1.0.
  • Подписанты зависят только от PSR-18, PSR-17 и PSR-3. Никакой SDK AWS, Azure или Google не требуется и не поставляется.
  • Пробуйте supportsAlgorithm() до подписи, чтобы несовместимый провайдер отклонялся на этапе выбора, а не в середине сессии.
  • Поля учётных данных внедряются через конструктор и помечены как чувствительные параметры. Сообщения лога несут только структурные поля; ни учётные данные, ни токен, ни содержимое документа в логи не записываются.
  • Привязывайте версии ключа явно в регулируемых развёртываниях. Умолчания разрешения алиаса (AWS) и последней включённой версии (Azure) удобны, но не детерминированы при ротациях.
  • Сторонние драйверы реализуют KmsSignerInterface и должны выделять свой providerId() в пространство имён во избежание коллизий с зарезервированными встроенными идентификаторами.

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