Skip to content

Long-Term Validation

Pro — Commercial License Required
Long-Term Validation (LTV) requires the Pro package.

LTV ensures signatures remain verifiable after certificates expire or revocation services go offline by embedding all validation data into the PDF via the Document Security Store (DSS).

LTV Classes

ClassPurpose
LtvManagerOrchestrates chain building, OCSP/CRL fetching, DSS assembly
DssBuilderBuilds the DSS dictionary with certs, OCSPs, CRLs
OcspClientFetches OCSP responses (RFC 6960)
CrlFetcherDownloads CRLs from distribution points (RFC 5280)

LtvManager

At B-LT or B-LTA levels, LtvManager runs automatically inside DigitalSigner. For manual control:

php
use Yeeefang\TcpdfNext\Pro\Security\Ltv\LtvManager;

$ltv = new LtvManager($pdf);
$ltv->addCertificate(file_get_contents('/certs/intermediate.pem'));
$ltv->addCertificate(file_get_contents('/certs/root.pem'));
$ltv->addOcspResponse($ocspResponseDer);
$ltv->addCrl($crlDer);
$ltv->apply(); // builds and embeds the DSS dictionary

DssBuilder

Constructs the DSS dictionary (ISO 32000-2) containing /Certs, /OCSPs, /CRLs, and optional per-signature /VRI entries.

php
use Yeeefang\TcpdfNext\Pro\Security\Ltv\DssBuilder;

$dss = new DssBuilder();
$dss->addCertificate($intermediateDer);
$dss->addOcspResponse($ocspDer);
$dss->addCrl($crlDer);
$dss->addVri($sigHash, [$signerDer], [$ocspDer], [$crlDer]); // optional per-signature VRI
$dssDict = $dss->build();

OcspClient

Queries OCSP responders to check certificate revocation status.

php
use Yeeefang\TcpdfNext\Pro\Security\Ltv\OcspClient;

$ocsp = new OcspClient();
$ocsp->timeout(10);
$ocsp->cacheDir('/tmp/ocsp-cache');

$response = $ocsp->query(
    certificate:  '/certs/signing.pem',
    issuer:       '/certs/intermediate.pem',
    responderUrl: 'https://ocsp.example.com', // optional; extracted from AIA if omitted
);

echo $response->status();     // 'good', 'revoked', or 'unknown'
echo $response->producedAt(); // DateTimeImmutable
$derBytes = $response->toDer();

CrlFetcher

Downloads CRLs from the CDP declared in certificates, with optional disk caching.

php
use Yeeefang\TcpdfNext\Pro\Security\Ltv\CrlFetcher;

$fetcher = new CrlFetcher();
$fetcher->cacheDir('/tmp/crl-cache');
$fetcher->cacheTtl(86400); // 24 hours

$crl = $fetcher->fetchForCertificate('/certs/signing.pem');
echo $crl->issuerDN();
echo $crl->revokedCount();
$crl->isRevoked('01:AB:CD:EF'); // bool
$derBytes = $crl->toDer();

Certificate Chain Building

LtvManager follows the AIA caIssuers extension to discover intermediates automatically. If outbound HTTP is restricted, provide the chain manually via CertificateInfo::chain().

php
$ltv = new LtvManager($pdf);
$ltv->buildChain(signerCertificate: '/certs/signing.pem');
$chain = $ltv->chain(); // array of DER-encoded certificates

Archival Loop (B-LTA)

B-LTA adds a document timestamp after DSS embedding. Re-timestamp before the TSA certificate expires to maintain validity indefinitely:

Sign (B-B) -> TSA timestamp (B-T) -> DSS (B-LT) -> Document timestamp (B-LTA)
    -> [re-timestamp before expiry]
php
$ltv = LtvManager::load('/archive/contract-2026.pdf');
$ltv->retimestamp(new TsaClient('https://tsa.example.com/timestamp'));
$ltv->save('/archive/contract-2026.pdf');

Error Handling

LTV operations throw typed exceptions: OcspException (responder unreachable), CrlException (download failed), or ChainBuildException (incomplete chain). All are under the Yeeefang\TcpdfNext\Pro\Security\Ltv namespace.

Next Steps

Released under the LGPL-3.0-or-later License.