Enkripsi Lanjutan
Halaman ini mendokumentasikan implementasi enkripsi internal di TCPDF-Next Pro. Halaman ini membahas handler AES-256 AESV3, algoritma key derivation, normalisasi password, dan penanganan parameter sensitif. Jika Anda mencari penggunaan enkripsi dasar, lihat contoh Enkripsi AES-256.
AES-256 dengan Handler AESV3
TCPDF-Next Pro mengimplementasikan ISO 32000-2 (PDF 2.0) Standard Security Handler revisi 6, yang mewajibkan AES-256-CBC untuk semua enkripsi stream dan string. Handler diidentifikasi oleh /V 5 dan /R 6 di encryption dictionary.
use Yeeefang\TcpdfNext\Pro\Security\Aes256Encryptor;
$encryptor = new Aes256Encryptor(
ownerPassword: 'Str0ng!OwnerP@ss',
userPassword: 'reader2026',
);Mengapa Tanpa RC4 atau AES-128
TCPDF-Next Pro sengaja mengecualikan RC4 (40-bit dan 128-bit) dan AES-128:
| Algoritma | Alasan Pengecualian |
|---|---|
| RC4-40 | Rusak sejak 1995; bisa diserang dengan mudah |
| RC4-128 | Bias dalam keystream; dilarang oleh PDF 2.0 |
| AES-128 | Digantikan oleh AES-256 di revisi 6; tidak forward-compatible |
PDF 2.0 (ISO 32000-2:2020) mewajibkan AESV3 untuk dokumen baru. Mendukung algoritma yang lebih lemah akan mengkompromikan postur keamanan dan melanggar spesifikasi.
Algorithm 2.B: Key Derivation
File encryption key diturunkan dari password menggunakan Algorithm 2.B (ISO 32000-2, klausul 7.6.4.3.4). Ini adalah proses iteratif yang mengaitkan SHA-256, SHA-384, dan SHA-512:
function computeHash(password, salt, userKey = ''):
K = SHA-256(password || salt || userKey)
round = 0
lastByte = 0
while round < 64 OR lastByte > round - 32:
K1 = (password || K || userKey) repeated 64 times
E = AES-128-CBC(key = K[0..15], iv = K[16..31], data = K1)
mod3 = (sum of all bytes in E) mod 3
if mod3 == 0: K = SHA-256(E)
elif mod3 == 1: K = SHA-384(E)
else: K = SHA-512(E)
lastByte = E[len(E) - 1]
round += 1
return K[0..31] // 32-byte file encryption keyHashing iteratif ini membuat serangan brute-force mahal secara komputasional sambil tetap cukup cepat untuk penggunaan yang sah.
Komponen Key di Encryption Dictionary
| Entry | Panjang | Tujuan |
|---|---|---|
/O | 48 byte | Validasi owner password (hash + validation salt) |
/U | 48 byte | Validasi user password (hash + validation salt) |
/OE | 32 byte | File encryption key yang dienkripsi owner |
/UE | 32 byte | File encryption key yang dienkripsi user |
/Perms | 16 byte | Flag permission yang dienkripsi AES-256 |
Normalisasi Password SASLprep
Sebelum operasi kriptografis apa pun, password dinormalisasi menggunakan SASLprep (RFC 4013), yang merupakan profil dari stringprep (RFC 3454). Ini memastikan penanganan password yang konsisten terlepas dari bentuk normalisasi Unicode yang digunakan oleh sistem operasi atau input method.
use Yeeefang\TcpdfNext\Pro\Security\SaslPrep;
$normalized = SaslPrep::prepare('P\u{00E4}ssw\u{00F6}rd');
// Menormalisasi bentuk composed/decomposed, memetakan karakter tertentu,
// dan menolak codepoint yang dilarang.Apa yang Dilakukan SASLprep
- Map -- Karakter yang umumnya di-map-ke-nothing (misalnya, soft hyphen
U+00AD) dihapus. - Normalize -- String dikonversi ke Unicode NFC (Canonical Decomposition diikuti Canonical Composition).
- Prohibit -- Karakter dari RFC 3454 Tabel C.1.2 sampai C.9 ditolak (karakter kontrol, private use, surrogate, non-character, dll.).
- Bidirectional check -- String dengan karakter kiri-ke-kanan dan kanan-ke-kiri divalidasi sesuai RFC 3454 klausul 6.
Ini berarti user yang mengetik U+00FC (LATIN SMALL LETTER U WITH DIAERESIS) dan user lain yang mengetik U+0075 U+0308 (LATIN SMALL LETTER U + COMBINING DIAERESIS) akan menghasilkan encryption key yang sama.
Encoding Permission
Permission disimpan di entry /Perms sebagai blok terenkripsi AES-256-ECB 16 byte. Layout plaintext-nya:
Byte 0-3: Flag permission (little-endian int32)
Byte 4-7: 0xFFFFFFFF
Byte 8: 'T' jika EncryptMetadata true, 'F' jika tidak
Byte 9-11: 'adb'
Byte 12-15: Random paddingFlag permission mengikuti layout bit yang sama seperti yang didefinisikan di ISO 32000-2 Tabel 22.
Penanganan Stream Terenkripsi
Setiap content stream dan string di PDF dienkripsi secara individual:
- Initialization Vector (IV) 16 byte unik dihasilkan per stream/string menggunakan
random_bytes(16). - IV ditambahkan di awal ciphertext.
- Padding PKCS#7 diterapkan sebelum enkripsi.
- Filter
/Cryptdengan/AESV3diset pada semua parameter decode stream.
// Internal -- ditangani otomatis oleh writer
$iv = random_bytes(16);
$padded = pkcs7_pad($plaintext, blockSize: 16);
$encrypted = openssl_encrypt($padded, 'aes-256-cbc', $fileKey, OPENSSL_RAW_DATA, $iv);
$output = $iv . $encrypted;WARNING
TCPDF-Next Pro selalu mengenkripsi data stream. Flag /EncryptMetadata default ke true. Jika diset false, stream metadata XMP tetap tidak terenkripsi (berguna untuk search indexing), tapi semua stream lainnya tetap terenkripsi.
Penanganan Parameter Sensitif
Semua method yang menerima password dianotasi dengan atribut PHP 8.2 #[\SensitiveParameter]. Ini mencegah password muncul di stack trace, output debug, dan log error:
public function setOwnerPassword(
#[\SensitiveParameter] string $password,
): self {
$this->ownerPassword = SaslPrep::prepare($password);
return $this;
}Jika exception terjadi, stack trace akan menampilkan Object(SensitiveParameterValue) alih-alih string password yang sebenarnya.
Contoh Lengkap
use Yeeefang\TcpdfNext\Core\Document;
use Yeeefang\TcpdfNext\Pro\Security\Aes256Encryptor;
use Yeeefang\TcpdfNext\Pro\Security\Permissions;
$pdf = Document::create()
->setTitle('Confidential Report')
->addPage()
->setFont('Helvetica', size: 12)
->multiCell(0, 6, 'This document is protected with AES-256 encryption.');
$encryptor = new Aes256Encryptor(
ownerPassword: 'Str0ng!OwnerP@ss',
userPassword: 'reader2026',
permissions: new Permissions(
print: true,
printHighQuality: false,
copy: false,
modify: false,
annotate: true,
fillForms: true,
extractForAccess: true,
assemble: false,
),
);
$pdf->encrypt($encryptor)
->save(__DIR__ . '/encrypted.pdf');