Skip to content
getnextpdf.com

Security and signing errors

This page documents the security-domain exceptions in the NextPDF\Security namespace tree. Each entry names the class, states when it is thrown, lists the fields its getContext() returns, and gives a recovery step.

Most of these classes extend SecurityException, which extends NextPdfException and implements ContextAwareExceptionInterface. That means getContext(): array returns structured, secret-free diagnostics you can route to logging or application performance monitoring (APM) pipelines. Catch SecurityException to trap every security-domain failure in one block; catch a specific subclass when you need its typed payload.

A few classes in this tree extend RuntimeException directly rather than SecurityException. Those are marked below; they do not expose getContext(), and most are documented as internal control-flow signals you should not expect to catch in application code.

AspectBehavior
Base contractNextPdfException::getContext() returns []; subclasses override it.
Secret hygieneMessages and context omit raw key material, plaintext, PINs, and initialization vector (IV) bytes. Keys are surfaced only as a fingerprint prefix.
SecurityExceptionAbstract base; carries no fields of its own. Subclasses define the payload.
  • When thrown. Never thrown directly; it is the abstract base for the security domain. It exists so a single catch (SecurityException $e) block can trap authenticated-encryption integrity failures, nonce-reuse defenses, the PDF/A-versus-encryption binding, key-management faults, and PKI failures.
  • Context fields. None of its own. Inherits the empty default from NextPdfException; subclasses populate the payload.
  • Recovery. Catch the concrete subclass for actionable handling, or SecurityException for coarse security-incident routing.

Encryption and authenticated-encryption errors

Section titled “Encryption and authenticated-encryption errors”

These are raised by the AES-GCM (Galois/Counter Mode) encryptor and the PDF/A guard. For symptom-first guidance, see Encryption and permissions.

  • When thrown. An authenticated-encryption with associated data (AEAD) decryption fails for a non-tampering reason: truncated ciphertext, a missing IV, or a wrong key supplied at the API boundary, where there was not enough material for the integrity check to actually run. This is a configuration or transport error, not a security incident.
  • Context fields. algorithm (for example AES-256-GCM), reason (for example ciphertext shorter than IV+tag).
  • Recovery. Verify the ciphertext, IV, and key are complete and correctly framed; do not treat this as tampering. Contrast with TamperedDataException.
  • When thrown. The AEAD authentication tag fails verification. The tag covers ciphertext plus associated authenticated data (AAD); if either was modified after encryption, the underlying openssl_decrypt() returns false. This distinct subtype lets you surface a security-incident-grade alert rather than a framing error.
  • Context fields. algorithm, ciphertext_length (length of the rejected ciphertext, excluding IV and tag).
  • Recovery. Treat as tampering or a wrong key/IV. Do not retry blindly; investigate the source of the ciphertext. Per ISO/TS 32003:2023 §5.2 and NIST SP 800-38D §6.5, a failed tag check means the data is not authentic.
  • When thrown. AES-GCM is asked to encrypt twice with the same key and IV pair. The encryptor defends with a per-instance monotonic counter and, as defense in depth, a runtime hash-set of every emitted (key-fingerprint, IV) pair. Because the counter rules out collisions by construction, this firing is a critical-priority bug indicator that must never occur in production. Reusing a key/IV pair compromises the entire keystream (ISO/TS 32003:2023 §5.2 NOTE 2; NIST SP 800-38D §8.3).
  • Context fields. key_fingerprint_prefix (first 8 hex chars of SHA-256(key)), iv_length (always 12 for ISO/TS 32003), reason (hashset-collision or counter-rollover, distinguishing a counter-defeating refactor bug from the 2^63 counter trip-wire), and iv_fixed_field_hex (the IV fixed field, present only when supplied, reported under its own key and never mislabeled as the key fingerprint).
  • Recovery. Abort immediately and rotate the key. File a defect report; this indicates a bug in the encryptor, not bad caller input.
  • When thrown. An opt-in NIST SP 800-38D §8.3 safety-of-use invocation count is reached for a given AES-GCM key. This is a defense-in-depth telemetry hook for callers that want to enforce the spec-recommended bound (around 2^32 invocations per key) earlier than the architectural limits inside the encryptor. It does not fire by default; only the assertWithinSafetyBound() helper raises it.
  • Context fields. key_fingerprint_prefix, invocation_count (current encrypt() count, at or above the limit), invocation_limit (the opt-in bound).
  • Recovery. Rotate the document key (construct a fresh encryptor with new key material) before the cumulative collision and forgery probability stops being negligible, or expand caller policy to refuse continued service.
  • When thrown. An encryption operation is attempted on a PDF/A-tagged document. The PDF/A family (PDF/A-2, PDF/A-3, PDF/A-4) uniformly forbids encryption: per ISO 19005 §6.1.3 the Encrypt key shall not be present in the trailer, and ISO 19005-4:2020 Annexes A and B inherit this without modification. There is no permitted combination of PDF/A and encryption.
  • Context fields. pdfa_mode (for example pdfa4, pdfa3), encryption_operation (the rejected call, for example useAesGcm).
  • Recovery. To produce an encrypted document, omit the enablePdfA() call; to produce an archival document, omit the encryption call. See PDF/A and PDF/UA validation.
  • When thrown. A configured crypto policy rejects an algorithm, key strength, or cipher selected by a core signing, encryption, or hashing operation. It is the fail-closed boundary for compliance enforcement (for example FIPS 140-2/3, eIDAS, or custom enterprise policy) and is raised by CryptoPolicyEnforcer before any signature or ciphertext is produced, so a policy-violating operation can never emit a non-approved artifact. Distinct from a narrow OpenSSL operation failure and from a signing-primitive failure: this is a policy rejection of an otherwise valid request. Aligned with NIST SP 800-131A Rev. 2 and ISO/IEC 19790:2025 §7.
  • Context fields. policy (policy name, for example FIPS 140-3 Strict), category (hash, signature, encryption, or key-strength), item (the rejected item, for example an object identifier (OID), cipher name, or rsa/1024), reason.
  • Recovery. Select an algorithm, key length, or cipher that the named policy approves, or adjust the policy if you own it. Route the structured context to the documented compliance runbook.

Two same-named classes exist. They share the SecurityException root so one catch (SecurityException $e) block traps both, but they carry distinct payloads. Import by the fully qualified name when you need a specific shape.

KeyManagementException (lifecycle: NextPDF\Security\Exception)

Section titled “KeyManagementException (lifecycle: NextPDF\Security\Exception)”
  • When thrown. A key-management operation fails before the key is consumed by a sign or encrypt primitive: Privacy-Enhanced Mail (PEM), PKCS#12, or PKCS#11 key parse failures; key-derivation (HKDF, PBKDF2, scrypt) failures; AES Key Wrap (RFC 3394) rejection on a wrong key-encryption key; a hardware security module (HSM) returning malformed Distinguished Encoding Rules (DER); or an Ed25519 seed length mismatch.
  • Context fields. operation (for example load_pem, kek_derive, key_wrap), key_type (for example RSA, EC-P256, Ed25519, AES-256), reason. Raw key material is never included.
  • Recovery. Inspect the named operation and key type, fix the source key material or derivation input, and retry.

KeyManagementException (signing path: NextPDF\Security\Signature\Exception)

Section titled “KeyManagementException (signing path: NextPDF\Security\Signature\Exception)”
  • When thrown. A signer provider hits a key-management fault: the requested key version is unknown, disabled, scheduled for destruction, lacks signing permission, or is otherwise unusable. This is what RsaPssSigner and LocalKeySignerProvider throw on live key failures. Named constructors: unknownKeyVersion() and keyVersionDisabled().
  • Context fields. providerId, keyVersion, reason. Accessors: providerId(), keyVersion(), reason().
  • Recovery. Rotate or re-permission the key, or select a usable key version, then retry. Distinct from SignatureFailedException, which signals that the signing primitive itself failed.

For symptom-first guidance on unreachable levels and missing capabilities, see Signature and timestamp failures.

SignatureFailedException (R4-13: NextPDF\Security\Exception)

Section titled “SignatureFailedException (R4-13: NextPDF\Security\Exception)”
  • When thrown. A cryptographic signing operation fails: an RSA, ECDSA, or Ed25519 sign primitive returns false or wrong-length output; an HSM or PKCS#11 token responds with a non-success status; Cryptographic Message Syntax (CMS) SignedData assembly fails on a malformed certificate or chain; or an Ed25519 round-trip self-verify fails. New code should prefer this R4-13 subtype over the legacy PAdES-coupled signature exception.
  • Context fields. operation (for example sign, verify, build_cms), algorithm (for example rsa-pkcs1v15-sha256, ed25519), reason. Accessors: getOperation(), getAlgorithm(), getReason().
  • Recovery. Read the operation and algorithm, correct the input (key, certificate chain, or backend availability), and retry. Aligned with the fail-closed key-handling posture of ETSI EN 319 142-1.

SignatureFailedException (SPI: NextPDF\Security\Signature\Exception)

Section titled “SignatureFailedException (SPI: NextPDF\Security\Signature\Exception)”
  • When thrown. A SignerProviderInterface implementation cannot complete a signing operation for any reason not categorized as key-management: backend driver error, malformed key material, or unrecoverable HSM I/O. This is the catch-all for the fail-closed signing contract, where every primitive throws on failure rather than returning null, false, or an empty string. Named constructor: forProvider().
  • Context fields. providerId, reason. Accessors: providerId(), reason().
  • Recovery. Inspect the provider id and reason, fix the provider backend or key material, and retry. Branch on KeyManagementException versus this type to separate “the key is bad” from “the primitive failed”.
  • When thrown. The requested PAdES conformance level cannot be honored under the current runtime infrastructure (most often a missing timestamp authority for B-T and above) and the caller has not granted permission to degrade. The default is fail-closed: the engine refuses rather than silently producing a lower level while advertising the higher one, which would be an eIDAS-grade regression. Aligned with ETSI EN 319 142-1 §6. Note this class extends NextPdfException directly (not SecurityException).
  • Context fields. requestedLevel, highestAchievableLevel, reason. Accessors: requestedLevel(), highestAchievableLevel(), reason().
  • Recovery. Read reason to identify the missing infrastructure and supply it (for example configure a timestamp authority), or pass allowDegradation: true to PadesOrchestrator to intentionally accept the highest achievable level.
  • When thrown. SignerProviderRegistry::get() is asked for a provider id that is not registered. Implements PSR-11 NotFoundExceptionInterface, so the registry conforms to the PSR-11 container contract. Named constructor: forId(). This class extends RuntimeException and does not expose getContext().
  • Context fields. None. The unregistered id appears in the message.
  • Recovery. Register the provider under the expected id before requesting it, or correct the id you pass to the registry.

These extend RuntimeException and do not expose getContext(). SHAKE256 is the SHA-3 extendable-output function required by some ISO/TS 32001 paths.

  • When thrown. At digest time, when the selected provider cannot satisfy the request. Named constructors: noBackend() (no working SHAKE256 backend on this host, across all attempted tiers) and ffiCallFailed() (an FFI-bound OpenSSL EVP call returned a non-success status, for example from a stripped libcrypto build).
  • Context fields. None. The message names the attempted tiers or the failed symbol.
  • Recovery. Install ext-ffi with OpenSSL 3.x present, or upgrade to a PHP build that exposes shake256 in hash_algos(). A userland Keccak fallback is intentionally not shipped.
  • When thrown. From a SHAKE256 provider constructor when the capability probe fails, so the provider cannot be instantiated. It is a control-flow signal: the provider registry catches it, records the tier label, and tries the next tier. It should never escape into application code. Named constructor: forTier().
  • Context fields. None. The message names the tier and reason.
  • Recovery. Not caller-actionable directly; if the entire tier chain is exhausted, the registry surfaces Shake256NotAvailableException::noBackend() instead, which carries the operator-facing fix.

These cover the ISO/TS 32004 document-level message authentication code (MAC), stored under /AuthCode. Both extend NextPdfException and override getContext().

  • When thrown. Fail-closed, by the MAC token reader, when a CMS AuthenticatedData MAC token is structurally malformed or declares an algorithm outside the agreed ISO/TS 32004 set. Named constructors: malformed() and algorithmMismatch(). Marked @internal.
  • Context fields. status (the DocumentMacVerificationStatus value, either MalformedToken or AlgorithmMismatch). Public readonly property: $status.
  • Recovery. Treat the document as not verified. A malformed token or an algorithm outside the agreed set means the MAC cannot establish trust; do not proceed as if the content is protected.
  • When thrown. Fail-closed, when a document-level MAC verification cannot reach a trusted state: a missing or malformed /AuthCode, an algorithm outside the agreed set, an unwrap failure, or a MAC mismatch (tamper). The verifier’s verify() returns an explicit result for branching; this is the exception-flow counterpart thrown by assertVerified() so “trust the content” code can never proceed past an unverified document. Named constructor: fromResult().
  • Context fields. status (the DocumentMacVerificationStatus value). Public readonly property: $status.
  • Recovery. Do not trust the document content. Inspect status to distinguish a tamper (MAC mismatch) from a configuration issue (missing or malformed /AuthCode, algorithm mismatch).

These cover RFC 5280 certification-path validation. The base type and its subclasses are fail-closed.

  • When thrown. A strict-mode failure from the RFC 5280 path validator. It is the non-final base for narrower subclasses (ChainLengthExceededException, UnsupportedExtensionException), so handlers that catch this type also catch those via Liskov substitution. Extends SecurityException.
  • Context fields. Does not override getContext() (inherits the empty default). Carries the structured reasons in the frozen public readonly array property $reasons (a non-empty list of rule-name plus description strings).
  • Recovery. Read $reasons to identify the failing rule, fix the certificate chain, and re-validate. Catch this type to handle any path-validation failure uniformly.
  • When thrown. The path validator is asked to walk a chain whose length exceeds the configured ceiling. The cap is enforced before any parsing begins, so a malicious supplier cannot drive the validator into quadratic work or exhaust resources with an arbitrarily deep chain. The default ceiling of 10 follows the PKIX-CMP profile (RFC 4210 §5.3.18); real-world chains fit in 5 to 6 entries. Subclass of PkiPathValidationException.
  • Context fields. Inherits the empty getContext(); the reason string chain_length_exceeded: supplied=<n> cap=<n> is forwarded into the parent’s $reasons. Public readonly properties: $supplied, $cap.
  • Recovery. Supply a chain within the ceiling, or raise the configured cap if a legitimately longer chain is expected.
  • When thrown. The path validator encounters a critical X.509 extension whose enforcement is not yet implemented. Per RFC 5280 §4.2, an unrecognized critical extension must fail closed; both strict and lenient modes fail closed here, since silently skipping a critical extension would be a security regression. The validator covers chain build, AKI/SKI matching, key usage, extended key usage, basic constraints, expiry, and signature verification; anything else critical surfaces here. Subclass of PkiPathValidationException.
  • Context fields. Inherits the empty getContext(); structured reasons are forwarded into the parent’s $reasons. Public readonly properties: $extensionOid (dotted OID, for example 2.5.29.30 for name constraints), $extensionName, $clauseRef (pointer to the RFC 5280 clause and the deferred-items log entry).
  • Recovery. In lenient mode, catch this specific subclass to fall back to a coarser policy without swallowing real path-validation failures. Audit $extensionOid and $clauseRef against your PKI fixtures to see which extension is blocking validation.
  • When thrown. Both OCSP and certificate revocation list (CRL) endpoints are exhausted without a definitive verdict: OCSP transport failure or malformed response, and CRL transport failure or malformed CRL, with both circuit breakers open or both caches missing. Strict mode treats this as fail-closed; lenient mode catches it and emits a PSR-3 warning with revocation = null. Extends SecurityException.
  • Context fields. Does not override getContext() (inherits the empty default). Carries state in the public readonly properties $ocspState and $crlState (each defaults to unknown).
  • Recovery. Restore reachability to a revocation source, wait for circuit breakers to close, or warm the cache, then retry. Do not suppress this to obtain a long-term-validation artifact; the revocation assertion is part of that level.
  • When thrown. An RFC 6960 §4.2.2.2 BasicOCSPResponse signature fails cryptographic verification against the responder’s certificate. The parser decodes signatureAlgorithm (RSA-PSS, ECDSA, or RSA-PKCS1v15) and verifies signature over tbsResponseData; any failure raises this typed exception so callers can distinguish a structurally valid but cryptographically tampered response from a malformed-DER response. Non-final, so downstream packages can publish more specific subclasses. Extends SecurityException.
  • Context fields. Does not override getContext() (inherits the empty default). Carries the failure tag in the public readonly property $reason (for example signature_mismatch, responder_cert_not_in_bundle, unsupported_signature_algorithm); free-text detail is folded into the message.
  • Recovery. Inspect $reason. For responder_cert_not_in_bundle, supply the correct trust anchor bundle and responder certificate. For signature_mismatch, treat the response as untrustworthy. See Signature and timestamp failures.
  • When thrown. A failure in RFC 3161 time-stamp authority (TSA) communication or response parsing: the TSA returns an error status, the HTTP request fails, or the ASN.1 response cannot be parsed. It is the base of the TSA fault hierarchy and is non-final so verification failures can extend it. Extends NextPdfException.
  • Context fields. Does not override getContext() (inherits the empty default).
  • Recovery. Catch TsaException for any TSA-fault path. Verify TSA reachability and that the endpoint returns a well-formed RFC 3161 response.
  • When thrown. CMS verification of an RFC 3161 TimeStampToken fails at any of the mandated verification steps: RFC 5816 §3 ESSCertIDv2 binding, RFC 5652 §11 signed-attributes integrity, RFC 3161 §2.4.2 producedAt freshness, or RFC 5652 §5.4 SignerInfo signature. Fail-closed, with a typed step discriminator so audit pipelines can tell replay from clock skew from cert-mismatch without grepping messages. Subclass of TsaException, so legacy catch (TsaException) handlers continue to fire.
  • Context fields. step (the failing pipeline Step value) and message. Accessor: getStep().
  • Recovery. Actionable by a developer (misconfigured TSA certificate or skew tolerance) or security (suspected MITM or replay). Read step to localize the failing stage and fix the corresponding input or trust configuration.
  • When thrown. Internal signal that a DER walk hit a malformed or truncated boundary, raised by the low-level walkers inside the TSA token verifier. It is always caught at the public verify boundary and re-wrapped into a TsaTokenVerificationException carrying the proper step discriminator; it never leaks to caller code. Extends RuntimeException; marked @internal.
  • Context fields. None.
  • Recovery. Not caller-facing. Handle the wrapped TsaTokenVerificationException instead.

These extend RuntimeException and do not expose getContext(). Both are fail-closed decoders.

  • When thrown. The name-constraints decoder encounters an enforceable GeneralSubtree element it cannot faithfully decode. RFC 5280 §4.2.1.10 requires a relying party to process an enforceable name constraint or reject the certificate; converting the prior silent drop into this typed failure prevents a fail-open that would have silently widened the accepted name set. Scope is limited to the enforceable name forms (directoryName, dNSName, iPAddress, rfc822Name, uniformResourceIdentifier); non-enforceable forms remain ignorable and never raise it. Named constructor: undecodableEnforceableBase(). Marked @internal.
  • Context fields. None. A log-safe detail string is carried in the message.
  • Recovery. The enforcer surfaces a fail-closed name_constraints: reason and the chain is rejected. Investigate the certificate’s name-constraints encoding; do not relax enforcement.
  • When thrown. The qcStatements extension is structurally malformed: truncated DER, a wrong tag, or a length overflow. The decoder is fail-closed and raises rather than returning a partial or heuristic result when it cannot determine with certainty what the extension says. Marked @api.
  • Context fields. None.
  • Recovery. Catch it explicitly only if you intend to tolerate malformed encoding; otherwise treat the certificate’s qualified-certificate statements as undeterminable and reject or re-issue the certificate.
  • When thrown. A PKCS#11 v3.1 session-management defect. Every named constructor maps to a specific defect class and to a PKCS#11 CKR_* return value, exposed through the typed $kind discriminator so callers branch on a stable enum-string instead of fragile message matching. Constructors include: cryptokiNotInitialized(), userNotLoggedIn(), userAlreadyLoggedIn(), operationNotInitialized(), operationActive(), mechanismNotAllowed(), tokenDisconnected(), concurrentSessionLimitExceeded(), sessionAlreadyClosed(), stateTransitionInvalid(), osLockingRequired(), loginTtlExpired(), and signOperationTtlExpired(). Extends SecurityException.
  • Context fields. Does not override getContext() (inherits the empty default). Carries the typed kind in the public readonly property $kind, one of the KIND_* constants (for example KIND_USER_NOT_LOGGED_IN, KIND_TOKEN_DISCONNECTED, KIND_LOGIN_TTL_EXPIRED). Slot and session identifiers, mechanism, and TTL values appear in the message. PINs and certificate bytes are never included.
  • Recovery. Switch on $kind. For user_not_logged_in, log in with the user PIN before initializing a sign operation. For token_disconnected, treat all sessions on the slot as orphaned. For the TTL kinds, re-authenticate or re-initialize the operation. For mechanism_not_allowed, extend the configured mechanism allow-list or pick an allowed mechanism.