Skip to content
getnextpdf.com

Pro edition

Cloud KMS signing (AWS KMS, Azure Key Vault, GCP KMS)

NextPDF Pro signs a PDF with a key held in a cloud key-management service (KMS). The supported providers are Amazon Web Services (AWS) KMS, Microsoft Azure Key Vault, and Google Cloud Platform (GCP) Cloud KMS. Each provider implements one signing contract, so your application depends on the contract, not on a provider class. Only the signed-attributes digest is sent to the provider; the document never leaves your host for the signing operation. This page is behaviour-level: it states what each provider sends and receives, how key versions resolve, and where key custody stops being NextPDF’s responsibility.

The contract extends the Core hardware-and-cloud signer contract, so a cloud-KMS strategy plugs into the same signing path the Core signer uses.

Prerequisites are stated in the front matter and repeated under Prerequisites.

Cloud-KMS signing strategies ship in the nextpdf/pro package and are gated by the pro license feature flag. NextPDF Core ships a software CMS signer; NextPDF Enterprise adds hardware key custody through PKCS#11. Cloud-KMS signing is a Pro capability and is also reachable in Enterprise, since Enterprise depends on Pro. A deployment without an active Pro entitlement does not load these strategy classes; the Core signing contract continues to work unchanged. Compare editions.

Each cloud-KMS signer implements one provider contract that extends the Core signer contract. The contract adds three things: a stable provider identifier for registry lookup, a key-version-aware signing method, and self-description of the algorithms a provider supports so the orchestrator can pick a compatible provider before signing.

The signing flow keeps the document on your host:

  1. The Pro signing session computes the document digest and builds the CMS signed attributes.
  2. The session hashes the signed attributes and sends only that digest to the provider. An external signing service that accepts a caller-supplied message-digest and returns the signature is the established pattern for keeping the document inside your boundary, as described in the EU Digital Signature Service (DSS) reference framework.
  3. The provider signs the digest with the key version it resolves and returns the raw signature.
  4. The session assembles the CMS SignedData and embeds it in the PDF.

The providers are implemented over pure PSR-18 Hypertext Transfer Protocol (HTTP) calls — no cloud vendor software development kit (SDK) dependency. Authentication is delegated to your application: you supply a bearer token (AWS, GCP) or a token or service-principal credential (Azure). Each provider normalises its output for CMS: AWS and GCP return Rivest–Shamir–Adleman (RSA) signatures in DER form ready for CMS; an Elliptic Curve Digital Signature Algorithm (ECDSA) signature that a provider returns as a raw integer pair (Azure) is converted to the DER-encoded form, while GCP returns ECDSA already DER-encoded. The ECDSA curve and digest are paired conventionally — P-256 with SHA-256, P-384 with SHA-384, P-521 with SHA-512 — per the recommended pairing in RFC 5480.

A PSR-11 registry resolves providers by identifier and supports lazy factories. Enterprise self-host customers register a proprietary HSM or KMS driver by implementing the provider contract and binding it in the registry — without forking NextPDF Pro.

The providers expose different “active version” primitives, so the default key-version behaviour differs:

  • AWS KMS — a null key version uses the key alias, which AWS resolves to the current key version on the provider side.
  • Azure Key Vault — a null key version uses the unversioned key URL, which Azure resolves to the latest enabled version. An explicit override must be a 32-character hexadecimal identifier; any other value is rejected to prevent URL-segment injection.
  • GCP Cloud KMS — the asymmetric-sign endpoint operates on a specific crypto-key version only; there is no server-side “active version”. You must pin a version in the configuration or pass one explicitly. With neither set, the signer raises a key-management error rather than guessing.

Document which mode your deployment uses so the behaviour is deterministic.

  1. Install NextPDF Core and the Pro package, and hold an active Pro license.
  2. Provision a signing key in your chosen provider and note its identifiers (key alias or Amazon Resource Name for AWS; vault and key name for Azure; project, location, key ring, crypto key, and version for GCP).
  3. Provide a PSR-18 HTTP client and PSR-17 request and stream factories.
  4. Obtain the provider credential in your application: a bearer token for AWS or GCP, or a pre-obtained token or service-principal credentials for Azure. Token acquisition is your application’s responsibility; supply secrets from your secret manager, never from source.

Each provider has an immutable configuration object built from your identifiers and credentials. Common configuration concerns:

  • Provider identifieraws-kms, azure-keyvault, or gcp-kms, used as the registry lookup key.
  • Algorithm — selected per call from the algorithm name your signing session passes; the provider rejects an algorithm it does not support.
  • Key version — pinned in the configuration or passed per call, with the per-provider semantics described above.
  • Credential — a bearer token or service-principal credentials your application supplies from its secret manager.
  1. Build the provider configuration from your identifiers and a credential read from your secret manager.
  2. Construct the provider signer with the configuration, the signer certificate in DER form, the chain, the PSR-18 client, and the PSR-17 factories.
  3. Optionally register the provider in the PSR-11 registry under its identifier so the orchestrator resolves it by name.
  4. Run the Pro signing session: it computes the digest, builds signed attributes, and calls the provider with only the digest.
  5. Catch the most specific failure — key-management, unsupported-algorithm, or signature-failed — log a structural message without secrets, and rethrow.
examples/pro/kms-provider-registry.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Pro\Security\Signing\Kms\KeyManagementProviderRegistry;
use NextPDF\Pro\Security\Signing\Kms\KmsSignerInterface;
/**
* Register cloud-KMS providers behind one registry resolved by identifier.
*
* Each provider is supplied as a lazy factory so a provider is only
* constructed when first resolved. The caller depends on the registry and
* the provider contract, not on a concrete provider class.
*
* @param array<non-empty-string, callable(): KmsSignerInterface> $factories
* Provider factories keyed by provider identifier.
*
* @return KeyManagementProviderRegistry The populated registry.
*/
function buildKmsRegistry(array $factories): KeyManagementProviderRegistry
{
$registry = new KeyManagementProviderRegistry();
foreach ($factories as $providerId => $factory) {
$registry->registerFactory($providerId, $factory);
}
return $registry;
}
examples/pro/kms-sign-guarded.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Pro\Security\Signing\Kms\KmsSignerInterface;
use NextPDF\Pro\Security\Exception\KeyManagementException;
use NextPDF\Pro\Security\Exception\SignatureFailedException;
use NextPDF\Pro\Security\Exception\UnsupportedAlgorithmException;
use Psr\Log\LoggerInterface;
final readonly class KmsSigningService
{
public function __construct(
private KmsSignerInterface $provider,
private LoggerInterface $logger,
) {}
/**
* Sign a signed-attributes digest with a pinned key version.
*
* Only the digest is sent to the provider; the document stays on the
* host. Each failure mode is caught as its most specific type so the
* caller can distinguish a key-version problem from a transport failure.
*
* @param string $digest The signed-attributes digest to sign.
* @param string $algorithm The OpenSSL-style algorithm name.
* @param string|null $keyVersion The pinned key version, or null for the
* provider default (per-provider semantics).
*
* @throws KeyManagementException When the key version is unknown or required and absent.
* @throws UnsupportedAlgorithmException When the provider does not support the algorithm.
* @throws SignatureFailedException When the provider sign operation fails.
*
* @return string The raw signature bytes (DER for RSA and ECDSA per CMS rules).
*/
public function sign(string $digest, string $algorithm, ?string $keyVersion): string
{
try {
return $this->provider->signWithVersion($digest, $algorithm, $keyVersion);
} catch (KeyManagementException | UnsupportedAlgorithmException | SignatureFailedException $e) {
$this->logger->error('KMS signing failed', [
'provider' => $this->provider->providerId(),
'reason' => $e->getMessage(),
]);
throw $e;
}
}
}
  1. Confirm the provider self-describes the algorithm you intend to use before signing, so an unsupported algorithm is caught at selection rather than at the provider call.
  2. Confirm only the digest is transmitted: the document bytes must not appear in the provider request body. The request carries a base64-encoded digest, not the file.
  3. For ECDSA, confirm the embedded signature is DER-encoded — the signer converts a raw integer-pair signature for you.
  4. Open the signed PDF in a validator configured with your trust anchors and confirm the signature is reported as cryptographically intact. A produced signature is not a verified signature; the trust decision belongs to the verifier.
  5. Confirm no token, credential, or key material appears in your application logs.
  • The key stays in the provider. A cloud-KMS strategy is an integration point, not a key store. NextPDF Pro does not hold the private key for a KMS strategy.
  • Only the digest crosses the boundary. The session sends the signed-attributes digest to the provider, not the document — the message-digest-input pattern described in the EU DSS reference framework.
  • The byte range is computed by the engine. It is never accepted from the caller.
  • Fail-closed. A provider, network, key-version, or unsupported-algorithm failure raises a typed exception. The session does not silently produce an unsigned document and never substitutes a weaker algorithm.
  • Credentials are secrets. Tokens and service-principal credentials come from your secret manager and are excluded from logs.

Key protection depends on key handling, the configured KMS, and the deployment. NextPDF Pro provides KMS integration, not the key store. NextPDF Pro is FIPS-compatible only when configured against a FIPS-validated KMS or HSM.

  • Unknown or disabled key version. The provider maps a not-found or disabled-version response to a key-management exception that names the provider and key.
  • GCP without a pinned version. The GCP signer raises a key-management error when neither the configuration nor the call supplies a version, because the asymmetric-sign endpoint operates on a specific version only.
  • Unsupported algorithm. Requesting an algorithm the provider does not support raises an unsupported-algorithm exception before any network call.
  • Transport failure. A PSR-18 client error is mapped to a signature-failed exception; the session does not produce a partial result.
  • Missing credential. A signer with no token and no service-principal credentials raises a typed error rather than calling the provider unauthenticated.