Skip to content
getnextpdf.com

Enterprise edition

NextPDF Enterprise quick start

This tutorial takes you from an empty project to two working Enterprise results. First you verify an existing signed PDF and read its MainIndication. Then you raise a signed document to PAdES B-LT with the long-term producer. Every step shows the exact output or exception you should expect.

This capability ships in NextPDF Enterprise (nextpdf/enterprise) and activates with an Enterprise-tier license envelope. A deployment without that entitlement does not load the capability’s classes. Compare editions and get a license.

  • Composer is configured for the private NextPDF repository. Follow Install and authenticate first.
  • You have your Enterprise license envelope, downloaded from your account at app.getnextpdf.com. Licensing and activation explains what the envelope is and where it goes.
  • For step 3 you need a signed PDF to verify. For the B-LT part you also need your signer certificate and network access to OCSP/CRL responders.

Require the Enterprise package. It depends on nextpdf/core and nextpdf/pro, so Composer brings the whole stack:

Terminal window
composer require nextpdf/enterprise
composer show nextpdf/enterprise

If composer show prints the package and its version, the install worked. Now place the signed license envelope where your deployment loads it, exactly as Licensing and activation describes. Installing the package alone does not grant Enterprise capabilities; the activated license selects the edition.

Ask the entitlement evaluator what your license grants. Your bootstrap obtains the verified NextPDF\Enterprise\Licensing\LicenseKey during activation; pass it in:

<?php
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Licensing\EntitlementEvaluator;
use NextPDF\Enterprise\Licensing\LicenseKey;
/** @var LicenseKey|null $license The verified license from activation. */
$result = (new EntitlementEvaluator())->evaluate($license);
echo 'status: ' . $result->status->value . PHP_EOL;
echo 'edition: ' . ($result->edition?->value ?? 'none') . PHP_EOL;
echo 'runtime: ' . ($result->runtimeAllowed ? 'allowed' : 'disabled') . PHP_EOL;

With an active Enterprise license you see:

status: active
edition: enterprise
runtime: allowed

The method behind this step:

public function evaluate(?LicenseKey $license, ?DateTimeImmutable $now = null): EntitlementResult

Throws or fails with: it never throws. A missing license returns a fail-closed EntitlementResult with EntitlementStatus::NoLicense and runtimeAllowed false (see step 4).

Extract the signature from a signed PDF, then run basic AdES validation. The engine implements the validation levels of ETSI EN 319 102-1; validateBasic() is the clause 5.2 flow — structure, digest, signature crypto, and the certificate chain:

<?php
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Security\Validation\AdESValidationEngine;
use NextPDF\Enterprise\Security\Validation\CmsSignatureDataExtractor;
use NextPDF\Enterprise\Signature\SignatureExtractor;
$pdf = file_get_contents(__DIR__ . '/contract-signed.pdf');
if ($pdf === false) {
throw new RuntimeException('Could not read contract-signed.pdf');
}
$signatures = (new SignatureExtractor())->extract($pdf);
if ($signatures === []) {
throw new RuntimeException('The PDF carries no signature dictionary.');
}
$engine = new AdESValidationEngine(extractor: new CmsSignatureDataExtractor());
$report = $engine->validateBasic(
$signatures[0]['signedBytes'], // the exact /ByteRange-covered bytes
$signatures[0]['contents'], // the DER CMS SignedData from /Contents
);
echo $report->mainIndication->name . PHP_EOL;
echo ($report->subIndication?->name ?? '(none)') . PHP_EOL;

For a well-formed signature that passes the basic structural, digest, cryptographic, and chain checks configured in this example, you see:

TOTAL_PASSED
(none)

MainIndication has exactly three cases: TOTAL_PASSED, TOTAL_FAILED, and INDETERMINATE. The engine is fail-closed: a check it cannot positively establish yields INDETERMINATE, never a silent pass. A pass here is a validation outcome under this engine’s checks; trust anchors and long-term evidence belong to the deeper levels on the verification page.

public function extract(string $pdfData): array

Throws or fails with: InvalidArgumentException if the input is not a valid PDF. A malformed /ByteRange or /Contents yields empty strings (fail-closed), never a positive result.

public function validateBasic(string $signedData, string $signature): ValidationReport

Throws or fails with: it never throws on a verification failure. Every defect maps to a ValidationReport indication, e.g. HASH_FAILURE or SIG_CRYPTO_FAILURE.

Now upgrade a freshly signed document to B-LT. The long-term producer collects the certificate chain plus OCSP/CRL evidence and writes the Document Security Store (DSS). It continues the signing pass described on the Signature page, which gives you the output buffer, the object registry, and the signature /Contents hex:

use NextPDF\Enterprise\Security\Ltv\LtvManager;
use NextPDF\Security\Signature\CertificateInfo;
use NextPDF\Security\Signature\SignatureLevel;
$certInfo = CertificateInfo::fromPkcs12('/secure/signer.p12', $p12Password);
// $httpClient is any PSR-18 client; it fetches OCSP responses and CRLs.
$ltv = new LtvManager($certInfo, $httpClient, level: SignatureLevel::PAdES_B_LT);
// $buffer, $registry, and $signatureContentsHex come from the signing pass.
$dssObjectNumber = $ltv->enableLtv($buffer, $registry, $signatureContentsHex);

The return value is the DSS object number for the document catalog’s /DSS entry. The producer defaults to strict revocation enforcement: missing revocation material raises an exception instead of silently emitting a hollow “B-LT” file.

public function enableLtv(BinaryBuffer $buffer, ObjectRegistry $registry, string $signatureContentsHex): int

Throws or fails with: NextPDF\Enterprise\Security\Ltv\LtvException when chain validation fails, when the certificate is revoked, or when revocation material is missing under the strict default.

status: no_license — the envelope is not loaded

Section titled “status: no_license — the envelope is not loaded”

Step 2 prints status: no_license and runtime: disabled, and the result carries the warning No license configured. Enterprise runtime is disabled. Install a license or purchase one at https://nextpdf.dev/pricing. An entitlement-gated call then throws NextPDF\Accelerator\Exception\SpectrumAuthenticationException with code SPEC-LIC-001, e.g. Capability '...' requires a valid license. Fix: place and activate the envelope per Licensing and activation, then rerun step 2.

InvalidArgumentException: Input does not start with %PDF header

Section titled “InvalidArgumentException: Input does not start with %PDF header”

SignatureExtractor::extract() received something that is not a PDF — a wrong path, an empty read, or a compressed download. Check the file you loaded. An empty $signatures list is different: the file is a PDF, but it carries no /Type /Sig dictionary, so there is nothing to verify.

LtvException: Strict revocation: LTV warning: no revocation data for certificate at chain position 0

Section titled “LtvException: Strict revocation: LTV warning: no revocation data for certificate at chain position 0”

enableLtv() could not obtain an OCSP response or a CRL for a chain certificate, and the strict default refuses to write a B-LT claim without evidence. Check responder reachability from the host, or pass enforcementMode: RevocationEnforcementMode::PERMISSIVE only if you explicitly accept a warn-only run — never label such output as B-LT for production or compliance workflows unless the missing revocation evidence is explicitly accepted and documented. Related: requesting B-LTA without a TSA client fails with LtvException: TSA client required for document timestamps.