Menandatangani PDF dengan PAdES B-B, lalu memperluasnya ke PAdES B-T
Sekilas
Bagian berjudul “Sekilas”Gunakan resep ini untuk menghasilkan tanda tangan Portable Document Format (PDF) Advanced Electronic Signatures (PAdES) B-B: SignedData Cryptographic Message Syntax (CMS) dengan atribut tertanda tangan (content-type, message-digest, signing-time). Setelah itu, perluas tanda tangan tersebut ke PAdES B-T dengan menambahkan satu signature-time-stamp RFC 3161. B-T adalah B-B ditambah satu stempel waktu; ini bukan kelas tanda tangan tersendiri. Batas kepercayaannya eksplisit: menghasilkan tanda tangan tidak berarti verifikator akan memutuskan tanda tangan itu valid.
Peringatan U-1. NextPDF tidak menyatakan sertifikasi independen ETSI EN 319 142-1 apa pun untuk PAdES B-T. EN 319 142-1 tidak ada dalam korpus verifikasi; persyaratan
signature-time-stampB-T diverifikasi terhadap ETSI EN 319 122-1 §5.3 bersama dengan RFC 3161, RFC 5652, RFC 5816 dan ISO 32000-2 §12.8. Dukungan untuk profil B-T bukanlah sertifikasi kesesuaian atau keabsahan hukum; validator independenlah yang mengambil keputusan tersebut.
B-LT dan B-LTA (materi validasi Document Security Store (DSS), loop archival-timestamp) berada di luar cakupan resep ini dan bukan bagian dari permukaan penandatanganan Core/Pro yang dibahas di sini.
Pemasangan
Bagian berjudul “Pemasangan”composer require nextpdf/core:^3ext-openssl harus diaktifkan karena CertificateInfo mengurai kunci melalui OpenSSL. B-T juga memerlukan endpoint Time Stamping Authority (TSA) RFC 3161 yang dapat dijangkau serta klien HTTP PHP Standards Recommendation (PSR)-18.
Gambaran konseptual
Bagian berjudul “Gambaran konseptual”Tanda tangan PAdES B-B menyimpan SignedData CMS berenkode Distinguished Encoding Rules (DER) di entri Contents pada kamus tanda tangan; nilai Contents adalah string heksadesimal yang diberi padding melebihi digest byte-range (ISO 32000-2 §12.8.1). Digest mencakup berkas dan mengecualikan nilai tanda tangan itu sendiri (ISO 32000-2 §12.8.1).
PAdES B-T menambahkan tepat satu signature-time-stamp RFC 3161. Message imprint dari stempel waktu adalah hash dari oktet nilai tanda tangan SignerInfo, tanpa tag atau prefiks panjang Abstract Syntax Notation One (ASN.1) (ETSI EN 319 122-1 §5.3; RFC 3161 Appendix A). Token tersebut dibawa sebagai atribut tak tertanda tangan id-aa-timeStampToken, object identifier (OID) 1.2.840.113549.1.9.16.2.14 (RFC 3161 Appendix A), dan ditempatkan dalam SignerInfo.unsignedAttrs [1] IMPLICIT (RFC 5652 §5.3). Karena atribut tak tertanda tangan tidak dilindungi oleh tanda tangan (RFC 5652 §5.4), digest tertanda tangan B-B, /ByteRange, dan byte tanda tangan B-B tidak berubah — B-T hanya menambahkan stempel waktu. Sertifikat TSA diidentifikasi dengan ESSCertIDv2 (RFC 5816 memperbarui RFC 3161).
Peringatan U-1 (dinyatakan ulang pada klaim B-T). NextPDF tidak menyatakan sertifikasi ETSI EN 319 142-1 independen apa pun untuk PAdES B-T. EN 319 142-1 tidak ada dalam korpus verifikasi; persyaratan
signature-time-stampB-T diverifikasi terhadap ETSI EN 319 122-1 §5.3 bersama dengan RFC 3161, RFC 5652, RFC 5816, dan ISO 32000-2 §12.8. Dukungan untuk profil B-T bukanlah sertifikasi kesesuaian atau keabsahan hukum; validator independen yang mengambil keputusan tersebut.
SignatureLevel::PAdES_B_T tersedia di Core: SignatureLevel::PAdES_B_T->requiresTimestamp() bernilai true, ->isAvailableInEnvironment() bernilai true, dan ->requiresDss() bernilai false — B-T tidak menyertakan Document Security Store. B-T ≠ B-LT ≠ B-LTA: stempel waktu tanda tangan tidak menambahkan materi validasi atau stempel waktu arsip; itu adalah level terpisah yang lebih tinggi dan tidak dihasilkan di sini.
Diagram di bawah ini menunjukkan alur B-B lalu B-T sesuai urutan yang digunakan oleh mesin. ByteRange baru dihitung setelah seluruh berkas ditulis, sehingga offset akhir tidak dapat mengubah byte yang sedang di-hash. B-T kemudian menambahkan satu token RFC 3161 sebagai atribut tak tertanda tangan dan membiarkan digest tertanda tangan B-B tidak tersentuh.
Permukaan API
Bagian berjudul “Permukaan API”Titik masuk konfigurasi adalah Document::setSignature(CertificateInfo $certInfo, SignatureLevel $level = SignatureLevel::PAdES_B_B, ?TsaClient $tsaClient = null). Panggilan ini mencatat niat penandatanganan pada dokumen. Mesin penandatanganan PAdES Core (NextPDF\Security\Signature\DigitalSigner) menghasilkan tanda tangan kriptografis. Karena suite integrasi menguji mesin ini dan contoh yang dapat dijalankan memakainya secara langsung, keluarannya adalah objek CMS nyata yang dapat diurai. SignatureLevel::PAdES_B_T memerlukan TsaClient yang tidak null; membangun penanda tangan B-T tanpa itu akan melempar SignatureException.
API tingkat tinggi — satu panggilan, keluaran yang ditandatangani
Bagian berjudul “API tingkat tinggi — satu panggilan, keluaran yang ditandatangani”Jalur tercepat adalah API tingkat tinggi: konfigurasikan tanda tangan pada dokumen, lalu serialisasikan. Di baliknya, jalur ini menjalankan mesin PAdES Core yang sama (DigitalSigner). Ini hanyalah lapisan kemudahan tipis di atas panduan tingkat lebih rendah di bawah, bukan jalur kode tersendiri.
<?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');Seperti output() dan getPdfData(), save() menulis entri /Contents sebagai SignedData CMS berenkode DER di bawah SubFilter ETSI.CAdES.detached (ISO 32000-2 §12.8, §12.7.5.5; RFC 5652). Keluarannya dapat diverifikasi sebagai CMS — objek SignedData CMS yang berbentuk baik dan dapat dibaca oleh parser CMS — tetapi itu tidak sama dengan kesesuaian profil baseline ETSI EN 319 142-1 atau keabsahan hukum; validator independenlah yang mengambil keputusan tersebut (lihat peringatan U-1 di atas). Untuk B-T, panggilan tingkat tinggi menambahkan tepat satu signature-time-stamp RFC 3161 yang dijelaskan dalam gambaran konseptual; meneruskan TsaClient adalah satu-satunya perbedaan dari B-B.
Gunakan panduan DigitalSigner tingkat lebih rendah di bawah ini ketika Anda memerlukan kendali langsung atas algoritma, data byte-range, atau SignatureResult.
Contoh kode — Mulai cepat
Bagian berjudul “Contoh kode — Mulai cepat”<?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";Contoh kode — Produksi
Bagian berjudul “Contoh kode — Produksi”Program mandiri ini berjalan di bawah harness cookbook. Program ini mencerminkan examples/36-sign-pades-b-b-and-b-t.php. Program ini membangun sebuah dokumen, mengonfigurasinya untuk tanda tangan PAdES, lalu menandatanganinya pada B-B dan sekali lagi pada B-T dengan klien TSA. Di produksi, arahkan TsaClient ke endpoint RFC 3161 nyata melalui klien PSR-18 yang diperkeras: klien HTTP sadar keamanan yang menyematkan SubjectPublicKeyInfo (SPKI) TSA dan menyelesaikan Domain Name System (DNS) dengan aman. Untuk menjaga program ini tetap luring dan deterministik, program ini menyuntikkan klien TSA palsu pendukung pengujian dari repositori. Klien TSA palsu tersebut mengembalikan TimeStampResp RFC 3161 yang valid secara struktural.
<?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);STDOUT yang diharapkan (ukuran bervariasi tergantung sertifikat dan token TSA):
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).Peringatan U-1 (ditempatkan bersama klaim produksi B-T). NextPDF tidak menyatakan sertifikasi ETSI EN 319 142-1 independen apa pun untuk PAdES B-T. EN 319 142-1 tidak ada dalam korpus verifikasi; persyaratan B-T
signature-time-stampdiverifikasi terhadap ETSI EN 319 122-1 §5.3 bersama dengan RFC 3161, RFC 5652, RFC 5816, dan ISO 32000-2 §12.8. Dukungan untuk profil B-T bukanlah sertifikasi kesesuaian atau keabsahan hukum; validator independenlah yang mengambil keputusan tersebut.
Kasus tepi & jebakan
Bagian berjudul “Kasus tepi & jebakan”- B-T tanpa klien TSA. Membangun
DigitalSignerB-T tanpaTsaClientakan melemparSignatureException(TSA diperlukan untuk B-T). Amankan konfigurasi TSA sebelum Anda menandatangani. - Keterjangkauan TSA. B-T melakukan satu perjalanan bolak-balik RFC 3161 langsung per tanda tangan. Jika TSA terganggu, tanda tangan B-T tidak dapat dibuat. Gunakan circuit breaker dan service-level agreement (SLA) TSA yang sesuai dengan throughput Anda;
TsaClientmenerima circuit breaker. - Memperkeras klien HTTP TSA. Arahkan
TsaClientke klien PSR-18 yang menyematkan SubjectPublicKeyInfo TSA (SPKI, format RFC 7469) dan menyelesaikan Domain Name System (DNS) dengan aman;TsaClient::extractPublicKeyPin()menghasilkan pin dari sertifikat TSA. - B-T bukan B-LT/B-LTA. Stempel waktu tanda tangan tidak menyematkan materi validasi (sertifikat, Online Certificate Status Protocol (OCSP), certificate revocation list (CRL)) atau stempel waktu arsip. Itu adalah level B-LT/B-LTA dan tidak dihasilkan oleh resep ini.
- Konflik linearisasi.
enableLinearization()dan tanda tangan yang dikonfigurasi saling eksklusif — salah satu panggilan melemparInvalidConfigExceptionketika yang lain sudah disetel. - Kunci HSM. Untuk kunci yang disimpan dalam hardware security module (HSM), bangun
CertificateInfodenganCertificateInfo::fromHsm(); kunci privat tidak pernah masuk ke memori proses. Kontrak penanda tangan PKCS#11 berada di Core; provider yang berfungsi tersedia di Premium.
Performa
Bagian berjudul “Performa”Tanda tangan B-B adalah operasi CMS lokal. B-T menambahkan satu perjalanan bolak-balik HTTP RFC 3161 sinkron ke TSA per tanda tangan. Anggarkan latensi TSA dan batas laju pada beban kerja batch. Gunakan TsaClient yang dilindungi circuit breaker.
Catatan keamanan
Bagian berjudul “Catatan keamanan”Tanda tangan yang dihasilkan tidak otomatis tepercaya. Apakah sebuah tanda tangan terverifikasi bergantung pada sertifikat, trust anchor-nya, dan kebijakan verifikator, yang semuanya berada di luar pustaka ini. Enkripsi melindungi kerahasiaan, bukan integritas; penandatanganan melindungi integritas dan keaslian, bukan kerahasiaan. Perlakukan penyimpanan kunci sebagai risiko utama: kunci perangkat lunak di memori proses hanya seaman host-nya.
Residensi Data & Mitigasi PII
Bagian berjudul “Residensi Data & Mitigasi PII”Operasi penandatanganan berjalan di dalam proses; byte dokumen dan kunci privat tidak meninggalkan host kecuali untuk perjalanan bolak-balik TSA B-T, yang hanya mengirim message imprint (sebuah hash dari nilai tanda tangan), tidak pernah konten dokumen (RFC 3161 §2.4.1 MessageImprint). Tidak ada teks dokumen atau informasi yang dapat mengidentifikasi pribadi (PII) yang dikirimkan ke TSA. Pilih TSA yang yurisdiksinya sesuai dengan kebijakan residensi data Anda.
Telemetri Aman & Pembersihan Log
Bagian berjudul “Telemetri Aman & Pembersihan Log”DigitalSigner menerima logger PSR-3 opsional. Logger ini mencatat algoritma dan level, bukan materi kunci atau byte tanda tangan. Parameter password pada CertificateInfo dan TsaClient ditandai #[SensitiveParameter], sehingga passphrase disensor dari stack trace. Jangan mencatat SignatureResult::$cmsSignedData atau $timestampToken.
Model ancaman
Bagian berjudul “Model ancaman”Dipertimbangkan: masukan yang dirusak setelah penandatanganan (terdeteksi oleh digest byte-range), kompromi kunci (di luar cakupan pustaka karena penyimpanan kunci adalah tanggung jawab integrator), peniruan TSA (dimitigasi oleh penyematan SPKI pada klien HTTP TSA), dan penurunan tingkat antar level (enum level bersifat eksplisit; mesin tidak menurunkan B-T ke B-B secara diam-diam). Yang tidak dinyatakan: tidak adanya kerentanan, atau bahwa tanda tangan yang dihasilkan sah secara hukum.
Perilaku mode FIPS
Bagian berjudul “Perilaku mode FIPS”Primitif penandatanganan disediakan oleh OpenSSL. Pada build OpenSSL yang tervalidasi Federal Information Processing Standards (FIPS), operasi RSA/ECDSA dan SHA-256 berjalan melalui provider FIPS; NextPDF sendiri tidak menyatakan validasi FIPS. CryptoCapabilities melaporkan primitif yang tersedia di host; verifikasi rantai provider OpenSSL dalam penyebaran Anda.
Kesesuaian
Bagian berjudul “Kesesuaian”| Pernyataan | Spesifikasi | Klausul | reference_id |
|---|---|---|---|
| Digest byte-range mencakup berkas dan mengecualikan nilai tanda tangan. | ISO 32000-2 | §12.8.1 | |
Contents menyimpan SignedData CMS DER; Contents document-timestamp menyimpan TimeStampToken. | ISO 32000-2 | §12.8.1 | |
Contents adalah string heksadesimal yang diisi melebihi digest byte-range. | ISO 32000-2 | §12.8.1 | |
| Imprint signature-time-stamp adalah hash dari oktet nilai tanda tangan SignerInfo (tanpa tag/length ASN.1). | ETSI EN 319 122-1 | §5.3 | |
| Nilai signature-time-stamp adalah SignatureTimeStampToken. | ETSI EN 319 122-1 | §6 | |
MessageImprint ::= SEQUENCE { hashAlgorithm, hashedMessage }. | RFC 3161 | §2.4.1 | |
Imprint stempel waktu tanda tangan adalah hash dari field tanda tangan SignerInfo; SignatureTimeStampToken ::= TimeStampToken. | RFC 3161 | App. A | |
OID id-aa-timeStampToken adalah 1.2.840.113549.1.9.16.2.14. | RFC 3161 | App. A | |
SignerInfo membawa unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL. | RFC 5652 | §5.3 | |
| Atribut tak tertanda tangan tidak dilindungi oleh tanda tangan; digest tertanda tangan B-B tidak berubah. | RFC 5652 | §5.4 | |
| RFC 5816 memperbarui RFC 3161; ESSCertIDv2 mengidentifikasi sertifikat TSA tanpa SHA-1. | RFC 5816 | §1 |
Resep ini menjelaskan bagaimana NextPDF menghasilkan tanda tangan B-B dan B-T. Resep ini tidak menyatakan bahwa tanda tangan yang dihasilkan sah secara hukum atau bahwa kesesuaian PAdES terpenuhi; validator independenlah yang mengambil keputusan tersebut.
Konteks komersial
Bagian berjudul “Konteks komersial”PAdES B-LT dan B-LTA (materi validasi DSS dan loop archival-timestamp) serta penyimpanan kunci HSM PKCS#11 disertakan dalam edisi Pro dan Enterprise. Resep ini hanya mencakup B-B dan B-T; level yang lebih tinggi merupakan kapabilitas yang berbeda dan diverifikasi secara terpisah serta berada di luar cakupan di sini.