Een PDF ondertekenen met PAdES B-B en daarna uitbreiden naar PAdES B-T
In een oogopslag
Sectie met titel “In een oogopslag”Gebruik dit recipe om een handtekening volgens Portable Document Format (PDF) Advanced Electronic Signatures (PAdES) B-B te maken: een Cryptographic Message Syntax (CMS) SignedData met ondertekende attributen (content-type, message-digest, signing-time). Breid die handtekening daarna uit naar PAdES B-T door één RFC 3161-signature-time-stamp toe te voegen. B-T is B-B plus één enkele tijdstempel; het is geen afzonderlijke handtekeningklasse. De vertrouwensgrens is expliciet: een handtekening produceren is niet hetzelfde als vaststellen dat een verifier die geldig vindt.
U-1-voorbehoud. NextPDF claimt geen onafhankelijke ETSI EN 319 142-1 certificering voor PAdES B-T. EN 319 142-1 maakt geen deel uit van het verificatiecorpus; de B-T-
signature-time-stamp-vereiste is geverifieerd tegen ETSI EN 319 122-1 §5.3 samen met RFC 3161, RFC 5652, RFC 5816 en ISO 32000-2 §12.8. Ondersteuning voor het B-T-profiel is geen conformiteits- of rechtsgeldigheidscertificering; een onafhankelijke validator doet die beoordeling.
B-LT en B-LTA (validatiemateriaal van de Document Security Store (DSS), archiveringstijdstempellus) vallen buiten het bereik van dit recipe en maken geen deel uit van het hier behandelde Core/Pro-ondertekeningsoppervlak.
Installatie
Sectie met titel “Installatie”composer require nextpdf/core:^3ext-openssl moet zijn ingeschakeld, omdat CertificateInfo sleutels via OpenSSL parseert. B-T heeft daarnaast een bereikbaar RFC 3161-eindpunt bij een Time Stamping Authority (TSA) en een HTTP-client volgens PHP Standards Recommendation (PSR)-18 nodig.
Conceptueel overzicht
Sectie met titel “Conceptueel overzicht”Een PAdES B-B-handtekening slaat een volgens Distinguished Encoding Rules (DER) gecodeerde CMS SignedData op in de Contents-vermelding van het handtekeningwoordenboek; de Contents-waarde is een hexadecimale tekenreeks die wordt opgevuld over de byte-range-digest (ISO 32000-2 §12.8.1). De digest dekt het bestand en sluit de handtekeningwaarde zelf uit (ISO 32000-2 §12.8.1).
PAdES B-T voegt precies één RFC 3161-signature-time-stamp toe. De message imprint van de tijdstempel is de hash van de octetten van de handtekeningwaarde in SignerInfo, zonder Abstract Syntax Notation One (ASN.1)-tag of lengteprefix (ETSI EN 319 122-1 §5.3; RFC 3161 Appendix A). Het token wordt meegedragen als het ongetekende attribuut id-aa-timeStampToken, object identifier (OID) 1.2.840.113549.1.9.16.2.14 (RFC 3161 Appendix A), geplaatst in SignerInfo.unsignedAttrs [1] IMPLICIT (RFC 5652 §5.3). Omdat ongetekende attributen niet door de handtekening worden beschermd (RFC 5652 §5.4), blijven de ondertekende B-B-digest, de /ByteRange en de B-B-handtekeningbytes ongewijzigd: B-T voegt alleen de tijdstempel toe. Het TSA-certificaat wordt geïdentificeerd met ESSCertIDv2 (RFC 5816 werkt RFC 3161 bij).
U-1-voorbehoud (herhaald bij de B-T-claim). NextPDF claimt geen enkele onafhankelijke ETSI EN 319 142-1-certificering voor PAdES B-T. EN 319 142-1 maakt geen deel uit van het verificatiecorpus; de B-T-
signature-time-stamp-vereiste is geverifieerd tegen ETSI EN 319 122-1 §5.3 samen met RFC 3161, RFC 5652, RFC 5816 en ISO 32000-2 §12.8. Ondersteuning voor het B-T-profiel is geen conformiteits- of rechtsgeldigheidscertificering; een onafhankelijke validator doet die beoordeling.
SignatureLevel::PAdES_B_T is beschikbaar in Core: SignatureLevel::PAdES_B_T->requiresTimestamp() is true, ->isAvailableInEnvironment() is true en ->requiresDss() is false — B-T maakt geen Document Security Store nodig. B-T ≠ B-LT ≠ B-LTA: een handtekeningtijdstempel voegt geen validatiemateriaal of archiveringstijdstempel toe; dat zijn afzonderlijke, hogere niveaus die hier niet worden geproduceerd.
Het onderstaande diagram toont de B-B- en daarna de B-T-stroom in de volgorde die de engine gebruikt. De ByteRange wordt pas berekend nadat het hele bestand is geschreven, zodat de uiteindelijke offsets de te hashen bytes niet kunnen wijzigen. B-T voegt vervolgens één RFC 3161-token toe als ongetekend attribuut en laat de ondertekende B-B-digest onaangeroerd.
API-oppervlak
Sectie met titel “API-oppervlak”Het configuratiepunt is Document::setSignature(CertificateInfo $certInfo, SignatureLevel $level = SignatureLevel::PAdES_B_B, ?TsaClient $tsaClient = null). Deze aanroep legt de ondertekeningsintentie op het document vast. De Core-PAdES-ondertekeningsengine (NextPDF\Security\Signature\DigitalSigner) produceert de cryptografische handtekening. Omdat de integratietestsuite deze engine test en het uitvoerbare voorbeeld deze rechtstreeks aanstuurt, is de uitvoer een echt, parseerbaar CMS-object. SignatureLevel::PAdES_B_T vereist een niet-null TsaClient; het construeren van een B-T-ondertekenaar zonder zo’n client leidt tot een SignatureException.
High-level-API — één aanroep, ondertekende uitvoer
Sectie met titel “High-level-API — één aanroep, ondertekende uitvoer”De snelste route is de high-level-API: configureer de handtekening op het document en serialiseer vervolgens. Onder de motorkap gebruikt deze dezelfde Core-PAdES-engine (DigitalSigner). Dit is een dunne gemakslaag bovenop de onderstaande stapsgewijze lower-level-uitleg, geen afzonderlijk codepad.
<?php
declare(strict_types=1);
use NextPDF\Core\Document;use NextPDF\Security\Signature\CertificateInfo;use NextPDF\Security\Signature\SignatureLevel;use NextPDF\Security\Timestamp\TsaClient;
$certInfo = CertificateInfo::fromPkcs12( p12Path: __DIR__ . '/signer.p12', password: 'p12-passphrase',);
// PAdES B-B end to end: configure, then serialise.$doc = Document::createStandalone();$doc->addPage();$doc->setFont('helvetica', '', 12);$doc->cell(0, 10, 'Signed end to end.', newLine: true);$doc->setSignature(certInfo: $certInfo, level: SignatureLevel::PAdES_B_B);$doc->save(__DIR__ . '/signed.pdf'); // or output() to stream, getPdfData() for bytes
// PAdES B-T: pass a TsaClient on the same call — one RFC 3161// signature-time-stamp is added (see the TsaClient hardening notes below).$doc->setSignature( certInfo: $certInfo, level: SignatureLevel::PAdES_B_T, tsaClient: $tsa,);$doc->save(__DIR__ . '/signed-bt.pdf');Net als output() en getPdfData() schrijft save() de /Contents-vermelding als een DER-gecodeerde CMS SignedData onder SubFilter ETSI.CAdES.detached (ISO 32000-2 §12.8, §12.7.5.5; RFC 5652). De uitvoer is CMS-verifieerbaar: een welgevormd CMS SignedData-object dat een CMS-parser kan lezen. Dat is niet hetzelfde als conformiteit met het ETSI EN 319 142-1-baselineprofiel of rechtsgeldigheid; een onafhankelijke validator doet die beoordeling (zie het U-1-voorbehoud hierboven). Voor B-T voegt de high-level-aanroep precies de ene RFC 3161-signature-time-stamp toe die in het conceptuele overzicht is beschreven; het doorgeven van de TsaClient is het enige verschil met B-B.
Gebruik de onderstaande lower-level-DigitalSigner-stapsgewijze uitleg wanneer u directe controle nodig hebt over het algoritme, de byte-range-gegevens of het SignatureResult.
Codevoorbeeld — Snelstart
Sectie met titel “Codevoorbeeld — Snelstart”<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Security\Signature\CertificateInfo;use NextPDF\Security\Signature\DigitalSigner;use NextPDF\Security\Signature\SignatureAlgorithm;use NextPDF\Security\Signature\SignatureLevel;
$certInfo = CertificateInfo::fromPkcs12( p12Path: __DIR__ . '/signer.p12', password: 'p12-passphrase',);
// PAdES B-B — a CMS SignedData, no timestamp.$signer = new DigitalSigner( certInfo: $certInfo, level: SignatureLevel::PAdES_B_B, algorithm: SignatureAlgorithm::Pkcs1v15,);$result = $signer->sign($byteRangeData);
echo $result->hasTimestamp() ? "B-T\n" : "B-B (no timestamp)\n";Codevoorbeeld — Productie
Sectie met titel “Codevoorbeeld — Productie”Dit zelfstandige programma draait onder de cookbook-harness. Het komt overeen met examples/36-sign-pades-b-b-and-b-t.php. Het bouwt een document, configureert het voor een PAdES-handtekening en ondertekent daarna als B-B en nogmaals als B-T met een TSA-client. Laat de TsaClient in productie wijzen naar een echt RFC 3161-eindpunt via een geharde PSR-18-client: een security-aware HTTP-client die de SubjectPublicKeyInfo (SPKI) van de TSA vastpint en het Domain Name System (DNS) veilig oplost. Om dit programma offline en deterministisch te houden, injecteert het de fake-TSA-client uit test-support in de repository. De fake-TSA-client retourneert een structureel geldige RFC 3161-TimeStampResp.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Security\Signature\CertificateInfo;use NextPDF\Security\Signature\DigitalSigner;use NextPDF\Security\Signature\SignatureAlgorithm;use NextPDF\Security\Signature\SignatureLevel;use NextPDF\Security\Timestamp\TsaClient;use NextPDF\Tests\Support\FakeTsaHttpClient;
// In your application, build CertificateInfo from your own signing material:// CertificateInfo::fromPkcs12($p12Path, $passphrase) — a .p12/.pfx bundle// CertificateInfo::fromFiles($certPem, $keyPem, $pass) — separate PEM files// This program uses the repository RSA-2048 test fixtures so it is offline.$certDir = __DIR__ . '/tests/Fixtures/Certificates';$certPath = $certDir . '/test-rsa-2048-cert.pem';$keyPath = $certDir . '/test-rsa-2048-key.pem';
if (!is_file($certPath) || !is_file($keyPath)) { fwrite(STDERR, "Certificate fixtures absent. Run tests/Fixtures/Certificates/generate.sh\n"); exit(1);}
$certInfo = new CertificateInfo( certificate: (string) file_get_contents($certPath), privateKey: (string) file_get_contents($keyPath),);
// Build the document and record the signing intent on it. The ByteRange// digest input is the document bytes with the /Contents placeholder// excluded (ISO 32000-2 §12.8); getPdfData() yields the bytes to hash.$doc = Document::createStandalone();$doc->setTitle('Signed Invoice 2026-0042');$doc->setAuthor('NextPDF Cookbook');$doc->addPage();$doc->setFont('helvetica', '', 12);$doc->cell(0, 10, 'This document is configured for a PAdES signature.', newLine: true);$doc->setSignature(certInfo: $certInfo, level: SignatureLevel::PAdES_B_B);
$byteRangeData = $doc->getPdfData();
// --- PAdES B-B: a CMS SignedData, no timestamp ---$bb = (new DigitalSigner( certInfo: $certInfo, level: SignatureLevel::PAdES_B_B, algorithm: SignatureAlgorithm::Pkcs1v15,))->sign($byteRangeData);
// --- PAdES B-T: B-B + one RFC 3161 signature-time-stamp ---// In production, build the TsaClient with your TSA endpoint and a hardened// PSR-18 client (use the security-aware HTTP client for SSRF/DNS pinning):// $tsa = new TsaClient(// tsaUrl: 'https://tsa.example.com/timestamp',// httpClient: $hardenedPsr18Client,// );// Here the offline fake TSA client keeps the program network-free.$tsa = new TsaClient( tsaUrl: 'https://tsa.example.com/timestamp', httpClient: new FakeTsaHttpClient(),);$bt = (new DigitalSigner( certInfo: $certInfo, tsaClient: $tsa, level: SignatureLevel::PAdES_B_T, algorithm: SignatureAlgorithm::Pkcs1v15,))->sign($byteRangeData);
// B-T = B-B + a single timestamp token. The B-B signed digest is unchanged;// $bt->timestampToken holds the DER-encoded RFC 3161 token.printf("PAdES B-B CMS: %d bytes, timestamp=%s\n", $bb->getSize(), $bb->hasTimestamp() ? 'yes' : 'no');printf( "PAdES B-T CMS: %d bytes, timestamp=%s (%d-byte RFC 3161 token)\n", $bt->getSize(), $bt->hasTimestamp() ? 'yes' : 'no', strlen($bt->timestampToken),);echo "B-T = B-B + one RFC 3161 signature-time-stamp (unsigned attribute).\n";
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script under the// semantic profile (the signed CMS/timestamp bytes are inherently// non-reproducible and are asserted by the PHPUnit harness, not a byte hash).$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');file_put_contents($out !== false && $out !== '' ? $out : __DIR__ . '/signed-invoice.pdf', $byteRangeData);Verwachte STDOUT (de groottewaarden variëren met het certificaat en het TSA-token):
PAdES B-B CMS: <n> bytes, timestamp=noPAdES B-T CMS: <n> bytes, timestamp=yes (<m>-byte RFC 3161 token)B-T = B-B + one RFC 3161 signature-time-stamp (unsigned attribute).U-1-voorbehoud (samen geplaatst bij de B-T-productieclaim). NextPDF claimt geen enkele onafhankelijke ETSI EN 319 142-1-certificering voor PAdES B-T. EN 319 142-1 maakt geen deel uit van het verificatiecorpus; de B-T
signature-time-stamp-vereiste is geverifieerd tegen ETSI EN 319 122-1 §5.3 samen met RFC 3161, RFC 5652, RFC 5816 en ISO 32000-2 §12.8. Ondersteuning voor het B-T-profiel is geen conformiteits- of rechtsgeldigheids- certificering; een onafhankelijke validator doet die beoordeling.
Randgevallen & valkuilen
Sectie met titel “Randgevallen & valkuilen”- B-T zonder een TSA-client. Het construeren van een B-T-
DigitalSignerzonderTsaClientleidt tot eenSignatureException(de TSA is vereist voor B-T). Bescherm de TSA-configuratie voordat u ondertekent. - TSA-bereikbaarheid. B-T voert per handtekening een live RFC 3161-rondrit uit. Een TSA-storing betekent geen B-T-handtekening. Gebruik een circuit breaker en een service-level agreement (SLA) voor de TSA die past bij uw doorvoer; de
TsaClientaccepteert een circuit breaker. - De TSA-HTTP-client harden. Richt de
TsaClientop een PSR-18-client die de SubjectPublicKeyInfo (SPKI, RFC 7469-formaat) van de TSA vastpint en het Domain Name System (DNS) veilig oplost;TsaClient::extractPublicKeyPin()leidt de pin af uit het TSA-certificaat. - B-T is niet B-LT/B-LTA. Een handtekeningtijdstempel bevat geen validatiemateriaal (certificaten, Online Certificate Status Protocol (OCSP), certificate revocation list (CRL)) of een archiveringstijdstempel. Dat zijn de B-LT/B-LTA-niveaus; dit recipe produceert die niet.
- Linearisatieconflict.
enableLinearization()en een geconfigureerde handtekening sluiten elkaar uit — elke aanroep leidt totInvalidConfigExceptionwanneer de andere al is ingesteld. - HSM-sleutels. Bouw voor een sleutel die in een hardware security module (HSM) wordt bewaard
CertificateInfometCertificateInfo::fromHsm(); de privésleutel komt nooit in het procesgeheugen terecht. Het PKCS#11-ondertekenaarscontract is onderdeel van Core; een werkende provider hoort bij Premium.
Prestaties
Sectie met titel “Prestaties”Een B-B-handtekening is een lokale CMS-bewerking. B-T voegt per handtekening één synchrone RFC 3161-HTTP-rondrit naar de TSA toe. Houd in batchworkloads rekening met TSA-latentie en rate limits. Gebruik een met een circuit breaker beschermde TsaClient.
Beveiligingsnotities
Sectie met titel “Beveiligingsnotities”Een geproduceerde handtekening is geen vertrouwde handtekening. Of een handtekening succesvol verifieert, hangt af van het certificaat, het vertrouwensanker ervan en het beleid van de verifier, en die vallen buiten deze library. Versleuteling beschermt vertrouwelijkheid, niet integriteit; ondertekening beschermt integriteit en authenticiteit, niet vertrouwelijkheid. Beschouw sleutelbeheer als het primaire risico: een softwaresleutel in het procesgeheugen is slechts zo veilig als de host.
Gegevenslocatie & PII-mitigaties
Sectie met titel “Gegevenslocatie & PII-mitigaties”De ondertekeningsbewerking draait in-process; de documentbytes en de privésleutel verlaten de host niet. Voor de B-T-TSA-rondrit wordt alleen de message imprint verstuurd (een hash van de handtekeningwaarde), nooit documentinhoud (RFC 3161 §2.4.1 MessageImprint). Er wordt geen documenttekst of personally identifiable information (PII) naar de TSA verzonden. Kies een TSA waarvan het rechtsgebied aansluit op uw gegevenslocatiebeleid.
Veilige telemetrie & logscrubbing
Sectie met titel “Veilige telemetrie & logscrubbing”DigitalSigner accepteert een optionele PSR-3-logger. Die logt het algoritme en het niveau, niet het sleutelmateriaal of de handtekeningbytes. De password-parameters op CertificateInfo en TsaClient zijn gemarkeerd als #[SensitiveParameter], zodat wachtwoordzinnen in stack traces worden geredigeerd. Log de SignatureResult::$cmsSignedData of de $timestampToken niet.
Dreigingsmodel
Sectie met titel “Dreigingsmodel”Overwogen: gemanipuleerde invoer na ondertekening (gedetecteerd door de byte-range-digest), sleutelcompromittering (buiten het bereik van de library, omdat sleutelbeheer de verantwoordelijkheid van de integrator is), TSA-imitatie (beperkt door SPKI-pinning op de TSA-HTTP-client) en downgrade tussen niveaus (de niveau-enum is expliciet; de engine downgradet B-T niet stilzwijgend naar B-B). Niet geclaimd: dat er geen kwetsbaarheden zijn, of dat een resulterende handtekening rechtsgeldig is.
FIPS-modusgedrag
Sectie met titel “FIPS-modusgedrag”De ondertekeningsprimitieven worden geleverd door OpenSSL. Op een OpenSSL-build die is gevalideerd volgens de Federal Information Processing Standards (FIPS), draaien de RSA/ECDSA- en SHA-256-bewerkingen via de FIPS-provider; NextPDF claimt zelf geen FIPS-validatie. CryptoCapabilities rapporteert de op de host beschikbare primitieven; verifieer de OpenSSL-providerketen in uw deployment.
Conformiteit
Sectie met titel “Conformiteit”| Uitspraak | Specificatie | Clausule | reference_id |
|---|---|---|---|
| De byte-range-digest dekt het bestand en sluit de handtekeningwaarde uit. | ISO 32000-2 | §12.8.1 | |
Contents bevat DER CMS SignedData; een document-timestamp-Contents bevat een TimeStampToken. | ISO 32000-2 | §12.8.1 | |
Contents is een hexadecimale tekenreeks die wordt opgevuld over de byte-range-digest. | ISO 32000-2 | §12.8.1 | |
| De signature-time-stamp-imprint is de hash van de octetten van de handtekeningwaarde in SignerInfo (geen ASN.1-tag/length). | ETSI EN 319 122-1 | §5.3 | |
| De signature-time-stamp-waarde is een SignatureTimeStampToken. | ETSI EN 319 122-1 | §6 | |
MessageImprint ::= SEQUENCE { hashAlgorithm, hashedMessage }. | RFC 3161 | §2.4.1 | |
De imprint van de handtekeningtijdstempel is de hash van het handtekeningveld in SignerInfo; SignatureTimeStampToken ::= TimeStampToken. | RFC 3161 | App. A | |
id-aa-timeStampToken OID is 1.2.840.113549.1.9.16.2.14. | RFC 3161 | App. A | |
SignerInfo bevat unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL. | RFC 5652 | §5.3 | |
| Ongetekende attributen worden niet door de handtekening beschermd; de ondertekende B-B-digest is ongewijzigd. | RFC 5652 | §5.4 | |
| RFC 5816 werkt RFC 3161 bij; ESSCertIDv2 identificeert het TSA-certificaat zonder SHA-1. | RFC 5816 | §1 |
Dit recipe beschrijft hoe NextPDF een B-B- en een B-T-handtekening produceert. Het claimt niet dat een resulterende handtekening rechtsgeldig is of dat aan PAdES-conformiteit wordt voldaan; een onafhankelijke validator doet die beoordeling.
Commerciële context
Sectie met titel “Commerciële context”PAdES B-LT en B-LTA (DSS-validatiemateriaal en de archiveringstijdstempellus) en PKCS#11-HSM-sleutelbewaring worden geleverd in de Pro- en Enterprise-edities. Dit recipe behandelt alleen B-B en B-T; de hogere niveaus zijn afzonderlijke, apart geverifieerde mogelijkheden en vallen hier buiten het bereik.