KMS provider contract
At a glance
Section titled “At a glance”HsmSignerInterface is the public contract a third party implements to provide key custody for NextPDF. Your provider keeps custody of the private key. The engine builds the Cryptographic Message Syntax (CMS) structure.
Install
Section titled “Install”composer require nextpdf/core:^3Conceptual overview
Section titled “Conceptual overview”NextPDF separates signature assembly from key custody. The engine prepares the byte range and assembles the CMS SignedData structure. It does not hold the private key. Instead, it delegates key custody to a signing back end through a public contract.
You implement one of three public contracts:
SignerInterface. The base contract. It returns aSignatureResultfor the supplied data, applies a timestamp, and reports long-term validation support. Use this contract when the signing logic runs in process.HsmSignerInterface. The key-custody contract. The implementation must perform signing inside the hardware boundary. The private key must not leave that boundary. A key management system provider implements this contract.DeferredSignerInterface. An extension ofSignerInterfacefor asynchronous back ends. The caller submits data, receives a job identifier, and retrieves the result later.
This page specifies the public contract. It does not describe any internal NextPDF Pro or NextPDF Enterprise implementation. A paid edition can provide a supported implementation of this contract. You depend on the contract, not on the implementation.
Key custody requirement
Section titled “Key custody requirement”The contract requires that the private key never leaves the secure boundary. Established key-management practice supports this requirement. National Institute of Standards and Technology (NIST) SP 800-57 Part 1 Revision 5 treats key management as lifecycle handling for secure generation, storage, distribution, use, and destruction. Public-Key Cryptography Standards #11 (PKCS#11) v3.1 makes the boundary enforceable. When a private-key object sets CKA_SENSITIVE to true or CKA_EXTRACTABLE to false, the token must not reveal the key value in plaintext outside the token.
Your implementation is responsible for honoring this requirement. The engine cannot enforce it for you. If your sign() method can read raw key bytes into process memory, the contract loses its security property.
API surface
Section titled “API surface”NextPDF\Contracts\HsmSignerInterface (stable, since 1.0.0):
| Method | Returns | Purpose |
|---|---|---|
sign(string $data, string $algorithm) | string | Sign data inside the hardware boundary. Return raw signature bytes. Throw RuntimeException on failure. |
getCertificateDer() | string | Return the signer X.509 certificate in Distinguished Encoding Rules (DER) form. |
getCertificateChainDer() | array<string> | Return the intermediate certificates, issuer to root, excluding the signer certificate. |
getPublicKeyAlgorithm() | string | Return the public-key algorithm identifier. |
NextPDF\Contracts\SignerInterface (stable, since 1.0.0):
| Method | Returns | Purpose |
|---|---|---|
sign(string $data) | SignatureResult | Return the DER-encoded CMS SignedData. |
timestamp(string $signatureValue) | string | Return a DER-encoded Request for Comments (RFC) 3161 time-stamp token. |
supportsLtv() | bool | Report whether long-term validation (LTV) data can be embedded. |
NextPDF\Contracts\DeferredSignerInterface (experimental, since 3.0.0) extends SignerInterface with submitForSigning(), retrieveSignature(), and isComplete() for asynchronous back ends.
The signature levels the engine produces follow the PDF Advanced Electronic Signatures (PAdES) profiles. European Telecommunications Standards Institute (ETSI) EN 319 142-2 defines extended PAdES profiles that build on the baseline building blocks in EN 319 142-1. The engine maps levels B-B, B-T, B-LT, and B-LTA to those building blocks. Your signer supplies the cryptographic operation and the certificate material the engine needs.
Code sample — Quick start
Section titled “Code sample — Quick start”This minimal PKCS#11-style provider delegates signing to the token. PHP never reads the key value.
<?php
declare(strict_types=1);
use NextPDF\Contracts\HsmSignerInterface;
final class TokenSigner implements HsmSignerInterface{ public function __construct(private readonly TokenSession $session) {}
public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): string { // The token computes the signature. The key stays inside the token. return $this->session->c_sign($data, $algorithm); }
public function getCertificateDer(): string { return $this->session->readCertificate(); }
/** @return array<string> */ public function getCertificateChainDer(): array { return $this->session->readChain(); }
public function getPublicKeyAlgorithm(): string { return 'rsaEncryption'; }}Code sample — Production
Section titled “Code sample — Production”A production provider validates inputs, fails closed on a token error, and never logs key or signature material.
<?php
declare(strict_types=1);
use NextPDF\Contracts\HsmSignerInterface;use Psr\Log\LoggerInterface;use RuntimeException;
final class KmsSigner implements HsmSignerInterface{ public function __construct( private readonly RemoteKmsClient $kms, private readonly string $keyId, private readonly LoggerInterface $logger, ) {}
public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): string { if ($data === '') { throw new RuntimeException('Refusing to sign empty data'); }
try { // The KMS performs the operation. The key never reaches this process. return $this->kms->sign($this->keyId, $data, $algorithm); } catch (\Throwable $error) { // Fail closed. Do not log key material or signature bytes. $this->logger->error('kms.sign.failed', ['key' => $this->keyId]);
throw new RuntimeException('KMS signing failed', previous: $error); } }
public function getCertificateDer(): string { return $this->kms->certificate($this->keyId); }
/** @return array<string> */ public function getCertificateChainDer(): array { return $this->kms->chain($this->keyId); }
public function getPublicKeyAlgorithm(): string { return $this->kms->algorithm($this->keyId); }}Edge cases & gotchas
Section titled “Edge cases & gotchas”- Signature byte format. Return raw signature bytes. For Rivest-Shamir-Adleman (RSA), the bytes are DER. For Elliptic Curve Digital Signature Algorithm (ECDSA), the bytes are the raw
rvalue followed by thesvalue. - Chain order.
getCertificateChainDer()excludes the signer certificate. Order the intermediates from issuer to root. - Algorithm identifiers.
sign()uses OpenSSL-style identifiers.getPublicKeyAlgorithm()returns the X.509 algorithm name. - Failure is fail-closed. Throw
RuntimeExceptionon any token or KMS error. Do not return a partial or empty signature. - Key destruction. When a key reaches the end of its cryptoperiod, destroy it according to your key-management procedure. NIST SP 800-57 Part 1 Revision 5 §8.2.1.2 states that a key should be destroyed as soon as it is no longer needed. The destruction itself follows §8.3.4.
Data residency and PII mitigations
Section titled “Data residency and PII mitigations”The contract transfers only the data to sign and the certificate material. It does not transfer document content or personal data to the signing back end. Keep the byte range minimal. Do not place personal data in the signing reason or location fields. Those fields remain observable in the signed file.
Safe telemetry and log scrubbing
Section titled “Safe telemetry and log scrubbing”Never log the data passed to sign(), the returned signature bytes, the key identifier in a recoverable form, or any certificate private component. Log only the operation outcome and a non-reversible reference. The SignatureAppliedEvent hook is the supported audit anchor. See Action triggers.
Threat model
Section titled “Threat model”| Asset | Threat | Mitigation |
|---|---|---|
| Private key | Egress to process memory | Contract requires in-boundary signing; PKCS#11 CKA_SENSITIVE / CKA_EXTRACTABLE enforce non-extractability |
| Signing oracle | Unbounded signing requests | The implementation rate-limits and authenticates callers |
| Certificate chain | Substitution | The implementation returns a chain that builds to a trusted root |
| Signature bytes | Disclosure through logs | Fail-closed error path; no signature material in telemetry |
| Key past cryptoperiod | Continued use | Key destruction per NIST SP 800-57 Part 1 Revision 5 §8.2.1.2 |
Conformance
Section titled “Conformance”The signature levels conform to the PAdES profiles. ETSI EN 319 142-2 §5.1 defines extended PAdES profiles on the EN 319 142-1 baseline building blocks. The engine assembles those building blocks from the material your signer supplies. Key management aligns with the NIST SP 800-57 Part 1 Revision 5 lifecycle, including the §8.2.1.2 destruction requirement. Hardware key custody aligns with the PKCS#11 v3.1 non-extractable-key attributes. Citations are recorded in the page front matter.
Export control and review governance
Section titled “Export control and review governance”This page covers PKCS#11 and hardware security module (HSM) key custody, so its front matter sets export_control_class: legal-review-required. Under the documentation review policy (plan §17 gate 6), any page with a security mode, path, or content matching PAdES, FIPS, HSM, or PKCS#11 requires sign-off from the @nextpdf-labs/crypto-reviewers GitHub team before it can publish. That CODEOWNERS approval is a hard merge gate: the page stays publish: false until both the legal export-control review and the @nextpdf-labs/crypto-reviewers review complete.
Commercial context
Section titled “Commercial context”NextPDF Enterprise provides a supported implementation of this contract with key management system custody, certificate-chain assembly, and audit integration. You can implement HsmSignerInterface yourself in Core or consume the Enterprise implementation through the same public contract without code changes.
See also
Section titled “See also”Related contracts and modules
Section titled “Related contracts and modules”- Signing contracts reference —
SignerInterface,HsmSignerInterface,DeferredSignerInterface, and the timestamp provider. - Security-policy contract reference — sibling security-sensitive Service Provider Interface (SPI) surface.
- SPI stability rules — the interface promise behind the
stablesigning contracts. - Action triggers and event listeners —
SignatureAppliedEvent, the supported audit anchor. - Extension authoring overview — the full public SPI surface.
The glossary defines key management system, cryptoperiod, and HSM; see the published glossary for each canonical definition.