Ga naar inhoud
getnextpdf.com

Enterprise editie

Batchvalidatie van handtekeningen

NextPDF Enterprise valideert digitale handtekeningen over veel PDF-documenten in één aanroep. NextPDF\Enterprise\Signature\BatchSignatureValidator::validate() neemt een lijst documenten aan en levert een BatchValidationReport. Elke handtekening doorloopt dezelfde fail-closed pipeline: cryptografische CMS-authenticatie over het gesigneerde byte range, trust-anchored validatie van de certificaatketen en OCSP/CRL-intrekkingscontrole. Het rapport bevat detail per document en per handtekening — CertChainStatus, RevocationStatus, TimestampStatus — zodat compliancetooling elk oordeel opnieuw kan afleiden uit het vastgelegde bewijs.

Het oordeelmodel is bewust strikt. Een handtekening is alleen Valid wanneer al het bewijs bevestigend is vastgesteld. Ontbrekend intrekkingsbewijs levert Indeterminate op, nooit Valid. Deze pagina behandelt de batch-orchestrator en de bijbehorende resultaattypes. De AdES-verifieerzijde voor één document is beschreven in Handtekeningverificatie. Het inbedden van materiaal voor langetermijnvalidatie is beschreven in Archive.

Deze mogelijkheid wordt geleverd in NextPDF Enterprise (nextpdf/enterprise) en wordt geactiveerd met een licentie-envelop op Enterprise-niveau. Een deployment zonder die entitlement laadt de klassen van de mogelijkheid niet. Vergelijk edities en verkrijg een licentie.

Terminal window
composer require nextpdf/enterprise

De nextpdf/premium metapackage lost ook het Enterprise-package op. Activatie gebruikt je Enterprise-licentie-envelop; zie Licenties en activatie. De batchtypes worden geautoload onder NextPDF\Enterprise\Signature. Er is geen PHP-extensie nodig buiten de basislijn van de engine.

Eén aanroep van validate() verwerkt een lijst van DocumentSignatureInput-waarden. Elke input bevat een document-identifier, de ruwe PDF-bytes en optionele PEM-gecodeerde trust anchors. De validator extraheert de handtekeningwoordenboeken van elk document en voert per handtekening drie fasen uit.

Fase 1 — cryptografische authenticatie. De losstaande CMS/PKCS#7-blob uit /Contents wordt geverifieerd over de bytes die /ByteRange dekt. De verifieerder herberekent de content-digest zelf en vergelijkt die met het messageDigest signed attribute. Hij vertrouwt nooit een digest die de producent aanleverde (RFC 5652 §5.6). De handtekeningwaarde moet verifiëren en het ondertekeningscertificaat moet aan de CMS gebonden zijn. Afwezige of misvormde /Contents of /ByteRange, een onparseerbare CMS, een digest-mismatch of een mislukte handtekeningcontrole falen allemaal closed. Een handtekening die verifieert onder SHA-1 wordt als zwak behandeld en is nooit een volledige pass.

Fase 2 — ketenvalidatie en trust anchoring. De uit de CMS herstelde signer-keten wordt gevalideerd als het prospectieve certificatiepad. De trustedCerts die je aanlevert vormen de trust-anchor-input, in de zin van RFC 5280 §6.1.1: het ketenterminus moet overeenkomen met een aangeleverd anchor via een DER SHA-256-vingerafdruk. Een structureel consistente keten waarvan het terminus geen geconfigureerd anchor is, wordt nooit als vertrouwd gerapporteerd. Zonder bruikbare anchors wordt alleen het structurele oordeel gerapporteerd en blijft CertChainStatus::$trusted false.

Fase 3 — intrekking. Intrekking draait over de herstelde keten na authenticatie, spiegelend aan het ETSI EN 319 102-1-model waarin intrekkingscontrole volgt op geslaagde padvalidatie (clause 5.2.6.2). OCSP is primair: alleen een cryptografisch geverifieerd antwoord telt, als Good of Revoked. Het CRL-pad is de fallback en attesteert de versheid van de lijst. Wanneer geen van beide clients geconfigureerd is, is de status unavailable.

Het oordeel per handtekening is een SignatureValidationStatus. De taxonomie spiegelt het statusmodel van ETSI EN 319 102-1 (TOTAL-PASSED / TOTAL-FAILED / INDETERMINATE) op granulariteit per handtekening:

BewijsOordeel
Certificaat bevestigd ingetrokkenInvalid (beslissend, ongeacht andere controles)
CMS-authenticatie mislukt, geen signer-materiaal hersteldError
CMS-authenticatie mislukt, signer-materiaal aanwezigInvalid
Geauthenticeerd, maar de keten valideert nietInvalid (of Error zonder keten)
Geauthenticeerd en keten-geldig, maar geen bevestigd trust anchorIndeterminate
Geauthenticeerd, keten-geldig, vertrouwd, maar geen sluitende niet-intrekkingIndeterminate
Al het bovenstaande bevestigend vastgesteldValid

De regel van sluitende niet-intrekking. “Niet bewezen ingetrokken” is niet hetzelfde als “bewezen niet ingetrokken”. Een Valid-oordeel vereist minstens één Good-intrekkingsresultaat. Een geverifieerd-goed OCSP-antwoord is de sluitende vorm: het bevestigt de eigen status van het signer-certificaat. Een cryptografisch geaccepteerde, verse CRL voldoet in deze implementatie ook aan de poort, maar alleen als een attestatie van versheid en integriteit — het pad parseert geen per-serie-invoeren, dus het levert geen intrekkingszekerheid per serienummer en nooit een positief revoked-oordeel. Configureer OCSP overal waar positieve intrekkingsdetectie ertoe doet: een CRL-only deployment brengt een ingetrokken certificaat niet naar boven als Invalid. Wanneer zowel het OCSP- als het CRL-resultaat Unknown of Unavailable is, is de intrekkingsstatus onbepaald en is het oordeel Indeterminate. Dit volgt ETSI EN 319 102-1: niet-beschikbare intrekkingsstatus-informatie resulteert in INDETERMINATE, nooit in een pass (clause 5.1.3, TRY_LATER). Dit is een gedragsverscherping in 3.1.0 met impact op achterwaartse compatibiliteit: eerdere releases konden Valid rapporteren zonder sluitend intrekkingsbewijs. Deployments die geen OCSP- of CRL-client configureren zien nu vaak Indeterminate waar ze eerder Valid zagen.

Twee grenzen kaderen deze mogelijkheid eerlijk in. Ten eerste evalueert de batchvalidator geen ingebedde tijdstempeltokens: TimestampStatus in batchresultaten is altijd de afwezige toestand. RFC 3161-tijdstempelevaluatie hoort bij de verifieerzijde voor één document; zie Handtekeningverificatie. Ten tweede is deze pagina alleen-lezen validatie. Het inbedden van DSS/VRI-materiaal voor langetermijngeldigheid is de Archive-mogelijkheid.

De dragende beslissing is een fail-closed oordeelproducent. Valid wordt alleen gemunt uit bevestigend bewijs op alle drie de assen: cryptografische authenticatie, een trust-anchored keten en sluitende niet-intrekking. Alles wat niet is vastgesteld degradeert naar Indeterminate in plaats van standaard een pass te worden, wat de houding van EN 319 102-1 is voor ontbrekend intrekkingsmateriaal. Batchdoorvoer koopt nooit rigor terug: de batchlaag is orchestratie over dezelfde geauditeerde CMS-verifieerder die voor één document wordt gebruikt, dus een run van 1.000 documenten past identieke cryptografie toe. Het rapport scheidt bovendien bewijs van oordeel — CertChainStatus en RevocationStatus leggen de inputs vast waarop elk oordeel rust, zodat een auditor het later opnieuw kan afleiden.

Ontwerpachtergrond: Ondertekenen op schaal, zonder compromis.

Alle onderstaande symbolen zijn publieke API in nextpdf/enterprise 3.1.0.

final class BatchSignatureValidator
{
public function __construct(
?SignatureExtractor $extractor = null,
?CertificateChainValidator $chainValidator = null,
private readonly ?OcspClient $ocspClient = null,
private readonly ?CrlFetcher $crlFetcher = null,
?CmsSignatureDataExtractor $cmsExtractor = null,
private readonly ClockInterface $clock = new SystemClock(),
)
public function validate(array $inputs): BatchValidationReport
}

Gooit of faalt met: validate() gooit \InvalidArgumentException als de inputlijst leeg is, en \OverflowException wanneer de batch meer dan 1.000 documenten telt. Een document dat geen parseerbare PDF is gooit niet; het wordt een Error-resultaat per document. De $clock is een PSR-20 Psr\Clock\ClockInterface die wordt gebruikt voor de CRL-versheidsbeslissing, zodat oordelen deterministisch zijn onder een bevroren testklok.

final readonly class DocumentSignatureInput
{
public string $documentId;
public function __construct(
string $documentId,
public string $pdfData,
public array $trustedCerts = [],
)
}

Gooit of faalt met: \InvalidArgumentException als $documentId een lege string is. $trustedCerts is een lijst van PEM-gecodeerde trust-anchor-certificaten.

final readonly class BatchValidationReport
{
public function __construct(
public array $documents,
public int $totalDocuments,
public int $totalSignatures,
public int $totalValid,
public int $totalInvalid,
public float $durationMs,
)
public function allValid(): bool
public function hasDocumentsWithoutSignatures(): bool
public function toJson(?CertPiiGuard $piiGuard = null): string
}

Gooit of faalt met: toJson() gooit \JsonException als het coderen mislukt. allValid() is alleen true wanneer er handtekeningen zijn en geen ervan niet-geldig is. Standaard past toJson() een privacy-by-default NextPDF\Enterprise\Signature\Eidas\CertPiiGuard toe, die de naam van de ondertekenaar, de root-issuer, de TSA-naam en de ketenprobleemdiagnostiek maskeert; zie eIDAS-assurancelevels voor de API van de guard.

DocumentValidationResult en DocumentValidationStatus

Sectie met titel “DocumentValidationResult en DocumentValidationStatus”
final readonly class DocumentValidationResult
{
public function __construct(
public string $documentId,
public DocumentValidationStatus $status,
public array $signatures,
public int $validCount,
public int $invalidCount,
)
public function hasSignatures(): bool
public function totalSignatures(): int
}
enum DocumentValidationStatus: string
{
case AllValid = 'all_valid';
case SomeInvalid = 'some_invalid';
case AllInvalid = 'all_invalid';
case NoSignatures = 'no_signatures';
case Error = 'error';
}

Gooit of faalt met: niets. Immutable value object en backed enum.

SignatureValidationResult en SignatureValidationStatus

Sectie met titel “SignatureValidationResult en SignatureValidationStatus”
final readonly class SignatureValidationResult
{
public function __construct(
public SignatureValidationStatus $status,
public CertChainStatus $certChain,
public TimestampStatus $timestamp,
public RevocationStatus $revocation,
public string $signer,
public string $level = '',
public string $subFilter = '',
public string $reason = '',
)
public function isValid(): bool
}
enum SignatureValidationStatus: string
{
case Valid = 'valid';
case Invalid = 'invalid';
case Indeterminate = 'indeterminate';
case Error = 'error';
}

Gooit of faalt met: niets. $signer is het door CMS geverifieerde certificaatsubject wanneer authenticatie slaagde, anders de lege string. $level is een van SubFilter afgeleid label (bijvoorbeeld B-B voor ETSI.CAdES.detached), geen bepaling van AdES-conformiteit.

final readonly class CertChainStatus
{
public function __construct(
public bool $valid,
public bool $trusted,
public int $chainLength,
public string $rootIssuer,
public array $issues = [],
)
public function hasIssues(): bool
}

Gooit of faalt met: niets. $trusted wordt alleen gezet bij een bevestigde hit op trust-anchor-lidmaatschap, nooit vanuit het niet-leeg zijn van de anchorlijst.

final readonly class RevocationStatus
{
public function __construct(
public RevocationCheckResult $ocspStatus,
public RevocationCheckResult $crlStatus,
public bool $isRevoked,
public ?DateTimeImmutable $revocationDate = null,
)
public static function unavailable(): self
public function hasConclusiveGood(): bool
}
enum RevocationCheckResult: string
{
case Good = 'good';
case Revoked = 'revoked';
case Unknown = 'unknown';
case Unavailable = 'unavailable';
}

Gooit of faalt met: niets vanuit de getoonde members. De klasse biedt ook bewijs-gecontroleerde statische factories (good(), revoked(), fromResults()), die \InvalidArgumentException gooien wanneer de geclaimde status het OCSP/CRL-bewijs tegenspreekt — een ingetrokken resultaat kan nooit als niet-ingetrokken worden gemunt, of andersom. hasConclusiveGood() is alleen true voor een niet-ingetrokken status waarbij minstens één controle Good is.

final readonly class TimestampStatus
{
public function __construct(
public bool $present,
public bool $valid,
public ?DateTimeImmutable $timestampTime = null,
public string $tsaName = '',
public array $issues = [],
)
public static function absent(): self
}

Gooit of faalt met: niets. In batchresultaten is dit altijd de absent()-toestand; zie Randgevallen & valkuilen.

Valideer één document en lees het rapport. Dit voorbeeld gebruikt een niet-ondertekende PDF, zodat de uitvoer deterministisch is.

batch-quick-start.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Signature\BatchSignatureValidator;
use NextPDF\Enterprise\Signature\DocumentSignatureInput;
// A minimal, unsigned PDF: the validator reports it as no_signatures.
$unsigned = "%PDF-1.7\n1 0 obj\n<< /Type /Catalog >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF\n";
$validator = new BatchSignatureValidator();
try {
$report = $validator->validate([
new DocumentSignatureInput(documentId: 'doc-001', pdfData: $unsigned),
]);
} catch (\InvalidArgumentException $e) {
// Empty input list, or an empty documentId.
echo 'Rejected: ' . $e->getMessage() . "\n";
exit(1);
}
echo 'Documents: ' . $report->totalDocuments . "\n";
echo 'Signatures: ' . $report->totalSignatures . "\n";
foreach ($report->documents as $doc) {
echo $doc->documentId . ': ' . $doc->status->value . "\n";
}
echo 'All valid: ' . ($report->allValid() ? 'yes' : 'no') . "\n";
echo 'Unsigned documents: ' . ($report->hasDocumentsWithoutSignatures() ? 'yes' : 'no') . "\n";

Verwachte uitvoer:

Documents: 1
Signatures: 0
doc-001: no_signatures
All valid: no
Unsigned documents: yes

Merk op dat allValid() hier no rapporteert: het vereist minstens één handtekening en geen enkel niet-geldig resultaat, zodat een lege handtekeningenverzameling nooit stilzwijgend slaagt.

Valideer een map met ondertekende contracten met intrekkingsclients, trust anchors, batch-chunking en een PII-afgeschermd JSON-rapport.

batch-validate-contracts.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Security\Ltv\CrlFetcher;
use NextPDF\Enterprise\Security\Ltv\OcspClient;
use NextPDF\Enterprise\Security\Ltv\OcspResponseCache;
use NextPDF\Enterprise\Signature\BatchSignatureValidator;
use NextPDF\Enterprise\Signature\DocumentSignatureInput;
use NextPDF\Enterprise\Signature\SignatureValidationStatus;
// Any PSR-18 client works; Guzzle shown here.
$httpClient = new \GuzzleHttp\Client(['timeout' => 10]);
// Revocation clients make a conclusive non-revoked (Good) result reachable.
// Without them, every verdict tops out at Indeterminate. The response cache
// lets repeat signers across the batch resolve without extra network calls.
$validator = new BatchSignatureValidator(
ocspClient: new OcspClient($httpClient, cache: new OcspResponseCache()),
crlFetcher: new CrlFetcher($httpClient),
);
// Trust anchors are an input: the chain terminus must match one of these.
$anchors = [(string) file_get_contents('/etc/nextpdf/trust/enterprise-root.pem')];
$inputs = [];
foreach (glob('/var/contracts/signed/*.pdf') ?: [] as $path) {
$inputs[] = new DocumentSignatureInput(
documentId: basename($path),
pdfData: (string) file_get_contents($path),
trustedCerts: $anchors,
);
}
$exit = 0;
// One call is capped at 1,000 documents; chunk larger runs.
foreach (array_chunk($inputs, 1000) as $batch) {
try {
$report = $validator->validate($batch);
// Signer PII is redacted by default in the serialized report.
file_put_contents('/var/log/nextpdf/batch-report.jsonl', $report->toJson() . PHP_EOL, FILE_APPEND); // one JSON document per line
} catch (\InvalidArgumentException | \OverflowException $e) {
fwrite(STDERR, 'Batch rejected: ' . $e->getMessage() . "\n");
exit(2);
} catch (\JsonException $e) {
fwrite(STDERR, 'Report encoding failed: ' . $e->getMessage() . "\n");
exit(3);
}
foreach ($report->documents as $doc) {
foreach ($doc->signatures as $sig) {
if ($sig->status !== SignatureValidationStatus::Valid) {
$exit = 1;
fwrite(STDERR, sprintf(
"%s: %s (chain trusted: %s, revoked: %s)\n",
$doc->documentId,
$sig->status->value,
$sig->certChain->trusted ? 'yes' : 'no',
$sig->revocation->isRevoked ? 'yes' : 'no',
));
}
}
}
}
exit($exit);

Verwachte uitvoer (stderr, voor één document waarvan het intrekkingsbewijs niet beschikbaar was; andere regels variëren met je inputs):

contract-0042.pdf: indeterminate (chain trusted: yes, revoked: no)

Het JSON-rapport serialiseert de identiteitsvelden van de ondertekenaar via de standaard CertPiiGuard, zodat een invoer per handtekening er zo uitziet (fragment, ter illustratie):

{
"status": "indeterminate",
"signer": "[REDACTED]",
"level": "B-B",
"subFilter": "ETSI.CAdES.detached"
}
  • Een lege inputlijst gooit \InvalidArgumentException; meer dan 1.000 documenten in één aanroep gooit \OverflowException. Chunk grotere runs, zoals in het productievoorbeeld.
  • Upgraden vanaf eerdere releases: zonder OCSP- of CRL-client geconfigureerd is intrekking unavailable, dus geen enkele handtekening kan Valid bereiken. Eerdere releases rapporteerden hier Valid; 3.1.0 rapporteert Indeterminate (zie Conceptueel overzicht).
  • Tellers op documentniveau zijn strikt: alleen Valid verhoogt validCount. Invalid, Indeterminate en Error verhogen allemaal invalidCount. Een document waarvan de enige handtekening Indeterminate is, rapporteert daarom all_invalid. Gate op status per handtekening wanneer het onderscheid ertoe doet.
  • De OCSP-controle draait alleen wanneer de herstelde keten minstens twee certificaten heeft, omdat de query de issuer nodig heeft. Een keten met één certificaat valt door naar het CRL-pad of naar unavailable.
  • crlStatus rapporteert nooit revoked in batchresultaten. De CRL-fallback attesteert alleen de versheid van de lijst; een gezaghebbend ingetrokken resultaat komt van OCSP.
  • timestamp is altijd absent() in batchresultaten. De batchvalidator evalueert geen ingebedde RFC 3161-tokens; gebruik Handtekeningverificatie voor tijdstempelevaluatie.
  • signer is leeg wanneer authenticatie mislukte. Wanneer gezet is het de subject CN (of O) van het door CMS geverifieerde certificaat — nooit de niet-geauthenticeerde /Name-string uit het handtekeningwoordenboek.
  • trustedCerts-invoeren moeten PEM-certificaten zijn. Een lege of misvormde anchorlijst levert een keten-oordeel dat alleen structureel is met trusted: false, wat het oordeel begrenst op Indeterminate.
  • Bytes die niet met een PDF-header beginnen produceren een error-status per document met nul handtekeningen — geen exception.
  • toJson() redigeert PII standaard. Geef new CertPiiGuard(disclosePii: true) alleen door waar je een gedocumenteerde wettelijke grondslag hebt om identiteit van de ondertekenaar te verwerken.
  • Fail-closed oordeelproducent. Valid vereist alles van: geverifieerde CMS-authenticatie over de /ByteRange-digest, een geldige keten, bevestigd trust-anchor-lidmaatschap en een sluitende niet-ingetrokken status. Elke niet-vastgestelde controle degradeert het oordeel; niets wordt standaard een pass.
  • Geen identity laundering. De gerapporteerde ondertekenaar is het cryptografisch gebonden certificaatsubject. De /Name-invoer is door de aanvaller controleerbare metadata en wordt nooit als ondertekenaar getoond.
  • Zwakke algoritmes slagen nooit. Een SHA-1-handtekening die verifieert wordt nog steeds als niet-geldig gerapporteerd; cryptografische geldigheid onder een zwakke digest wordt niet witgewassen tot een volledige pass.
  • Vertrouwen is een input, geen gevolgtrekking. Anchors die je aanlevert worden gematcht tegen het ketenterminus via een DER SHA-256-vingerafdruk (RFC 5280 §6.1.1). Zelfconsistentie van een keten, of een niet-lege anchorlijst alleen, stelt nooit vertrouwen vast.
  • Intrekking is beslissend. Een geverifieerde ingetrokken verklaring forceert Invalid ongeacht elke andere controle; niet-beschikbaar bewijs forceert Indeterminate.
  • Privacy standaard in geserialiseerde uitvoer. toJson() maskeert de signer CN, root-issuer DN, TSA-naam en ketenprobleemdiagnostiek tenzij je afziet van de standaard, wat GDPR Article 5(1)(c) dataminimalisatie implementeert op de serialisatiegrens.
  • Deterministische tijd. De CRL-versheidsbeslissing leest de geïnjecteerde PSR-20-klok, niet de wall clock van de host, zodat intrekkingsoordelen reproduceerbaar zijn onder test.

NextPDF Enterprise implementeert gedrag geïnformeerd door ETSI EN 319 102-1 (het drieledige validatiestatusmodel en de regel dat niet-beschikbare intrekkingsinformatie INDETERMINATE oplevert), RFC 5652 §5.6 (herberekening van de digest aan verifieerzijde) en RFC 5280 §6.1 (trust anchors als inputs van de relying party voor padvalidatie). Ondersteuning is geen conformiteit, en conformiteit is geen certificering. NextPDF houdt geen certificering en verleent er geen. De batchvalidator is geen gekwalificeerde validatiedienst, en de statussen ervan zijn engineering-oordelen afgestemd op de taxonomie van EN 319 102-1 — geen TOTAL-PASSED/TOTAL-FAILED/INDETERMINATE-indicaties uit een volledig validatieproces volgens clause 5. In het bijzonder voert batch-modus geen proof-of-existence of tijdstempelverwerking uit; de verifieerzijde voor één document dekt dat terrein.

De batchvalidator raadpleegt geen FIPS-modusbeleid, en het inschakelen van FIPS-modus verandert batch-oordelen niet. De algoritmeafhandeling aan verifieerzijde is vast en fail-closed: zwakke (SHA-1) handtekeningen worden nooit als Valid gerapporteerd, met of zonder FIPS-modus. Het Enterprise FIPS-modusbeleid gate’t de onderteken-/generatiezijde, gedocumenteerd in FIPS 140 — Deep Reference. FIPS 140-ondersteuning is een capaciteitsverklaring, geen validatie- of certificeringsclaim.

  • validate() gooit \InvalidArgumentException voor een lege lijst en \OverflowException boven 1.000 documenten. Misvormde documenten gooien nooit; ze produceren error-resultaten per document.
  • Valid vereist de conjunctie: CMS cryptografisch geverifieerd, keten geldig, trust-anchor-lidmaatschap bevestigd en RevocationStatus::hasConclusiveGood() true.
  • Een bevestigd-ingetrokken certificaat is beslissend: het oordeel is Invalid ongeacht al het andere bewijs.
  • Beide intrekkingscontroles Unknown/Unavailable betekent Indeterminate, nooit Valid (3.1.0-verscherping, impact op achterwaartse compatibiliteit).
  • Een geauthenticeerde, keten-geldige handtekening zonder een bevestigd trust anchor is Indeterminate — authentiek, vertrouwen niet vastgesteld.
  • signer is het door CMS geverifieerde subject of de lege string; de /Name-invoer wordt nooit gebruikt.
  • timestamp is altijd de afwezige toestand in batchresultaten.
  • validCount telt alleen Valid; alle andere statussen tellen mee in invalidCount, en de documentstatus wordt geaggregeerd uit die tellers.
  • toJson() past de privacy-by-default CertPiiGuard toe tenzij expliciet een guard wordt doorgegeven.
  • Rapporttotalen zijn exacte sommen over de resultaten per document; durationMs is de gemeten wandtijd voor de batch.

De Security / Signing-module van NextPDF Core is de producentzijde: hij maakt CMS-handtekeningen, past RFC 3161-tijdstempels toe en valideert ketens en intrekking voor het materiaal dat hij bij het ondertekenen inbedt. Core levert geen batch-orchestrator aan verifieerzijde: geen rapport over meerdere documenten, geen geaggregeerde statustaxonomie, geen OCSP/CRL-intrekkingsoordelen voor documenten van derden, en geen PII-afgeschermde rapportserialisatie. Op Core alleen zou je elke handtekening zelf extraheren en verifiëren en je eigen rapportage bouwen. De Enterprise-verifieerzijde voor één document (Handtekeningverificatie) en deze batch-orchestrator leveren die laag.

Deze pagina documenteert alleen extern waarneembaar gedrag en het ondersteunde publieke API-oppervlak. Interne namespacepaden, helperklassen, mechanismetabellen, runbook-bestandsnamen en ticketprefixen vallen buiten de scope.