Enterprise 版本
數位浮水印與隱寫式嵌入
NextPDF Enterprise 透過對字母對之間的間距做出微小、受控的調整,將一個隱藏、加密的酬載嵌入所產生的 PDF。你提供一個酬載——通常是一個逐收件者的識別碼——與一把密鑰;編碼器會將酬載寫成與文字自然字距難以察覺的偏移。一個相符的解碼器,在獲得相同金鑰時,能還原該酬載。本頁是行為層級的:它陳述編碼器寫入什麼、它所使用的密碼學,以及該技術的邊界。
預期用途是內部文件洩漏追蹤:當一份受控文件外洩時,還原出的標記可識別出該收件者的副本。它不是對抗式隱寫術,也不保證能挺過任意處理。
先決條件陳述於前置資料中,並於先決條件一節重述。
供應與授權
標題為「供應與授權」的區段此能力隨附於 NextPDF Enterprise(nextpdf/enterprise),並以一個 Enterprise 層級的授權封套啟用。缺少該授權的部署不會載入此能力的類別。此能力在文件產生期間完全於行程內執行;沒有任何文件內容離開主機。比較各版本並取得授權。
此能力的作用
標題為「此能力的作用」的區段以字距陣列繪製的 PDF 文字,在字符之間承載一個數值調整。編碼器將每個調整位置視為承載少量位元的載體:
- 它以一個認證式加密附帶關聯資料(AEAD)密碼加密酬載——預設為 AES-256-GCM,或 ChaCha20-Poly1305。AEAD 同時提供機密性與完整性,因此被竄改的載體會在解密時失敗,而非默默地產出錯誤的酬載。
- 它使用基於 HMAC 的金鑰衍生函式(HKDF)搭配 SHA-256,從你的密鑰與字型金鑰衍生出 32 位元組的加密金鑰。HKDF 會從輸入金鑰材料萃取出一把固定長度的偽隨機金鑰,再將其擴展到所需長度,依 RFC 5869 §2。
- 它為每次加密產生一個全新隨機的 12 位元組初始化向量(IV)。AES-GCM 要求對某把給定金鑰而言 IV 必須唯一,否則認證保證會喪失,依 NIST SP 800-38D §5.2.1。
- 它將加密後的位元組對應為一個位元序列,並將位元分散到可用的字母對位置上,每個位置編碼一或兩個位元。它對自然字距所加入的偏移,受一個可設定的 em 分數所限制——小到足以維持視覺上難以察覺。
解碼器逆轉此過程:它從一個內容串流讀取字距調整、減去自然字距、將偏移量化回位元、重新組裝加密的 blob,並以相同金鑰解密它。若金鑰錯誤或載體已被破壞,解密會回傳空值,而非錯誤的酬載。
容量隨文字長度而擴展:每個字母對位置承載一或兩個位元,因此一個酬載必須能塞進文字所提供的位置。當酬載超過容量時,編碼器會引發一個具型別的溢位錯誤。
一個 PDF/A 相容模式會將最大偏移減半,以維持在驗證器的寬度容差門檻之下,以容量換取更嚴格的一致性。
為何如此設計
標題為「為何如此設計」的區段承載此設計的關鍵選擇,是將標記藏在字距中,而非可見的疊加層或詮釋資料欄位。詮釋資料標記極易剝除,而可見的戳記會改變頁面。字距偏移則藏身於收件者必須保留的文字之中,並維持難以察覺。認證式加密是第二根支柱:被竄改或不完整的載體會通不過認證,因此解碼器回傳空值,而非錯誤的收件者。金鑰以 HKDF 逐字型衍生,將標記綁定至文件情境,而非一把裸露的共享密鑰。誠實的強健性揭露由此直接推導而出:標記能挺過一般的再散布,但無法挺過蓄意的內容串流改寫,因此其陳述範圍是內部洩漏追蹤,而非對抗式隱寫術。
設計背景:編校不是一塊黑色矩形。
先決條件
標題為「先決條件」的區段- 安裝 NextPDF Core 與 Enterprise 套件,並持有一份有效的 Enterprise 授權。
- 以一個會公開字距對度量的字型產生文件;編碼器會從字型度量讀取自然字距。
- 從你的密碼管理器(而非從原始碼)提供密鑰。解碼需要相同的金鑰。
- 依你的容量與一致性需求,決定位元深度(每個位置一或兩個位元)以及是否需要 PDF/A 相容。
編碼設定在建構時不可變且已驗證:
- 位元深度 — 每個字母對位置一或兩個位元。較高的深度提供更多容量,但偏移較大。
- 最大調整比率 — 偏移上限,以 em 的分數表示,在一個受限範圍內。較大的值提供更多餘裕,但有可見的風險。
- 密碼 — AES-256-GCM(預設)或 ChaCha20-Poly1305。兩者皆為 AEAD。
- PDF/A 相容 — 啟用時,將有效的最大偏移減半。
編碼與解碼請使用相同的設定;不符會導致無法還原任何酬載。
逐步操作
標題為「逐步操作」的區段- 從你的密碼管理器讀取密鑰。
- 建立編碼設定(位元深度、偏移比率、密碼、PDF/A 旗標)。
- 為你即將呈現的文字計算字距調整,傳入酬載、文字、字型金鑰、字型度量、密鑰,以及設定。
- 在你寫入文字 run 時套用回傳的調整,讓標記在產生期間被嵌入。
- 若要追蹤一份外洩的副本,以相同的字型金鑰、字型度量、密鑰與設定,對可疑文件的內容串流執行解碼器,並讀取還原出的酬載。
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
/** * Reject a payload that cannot fit the carrier text before encoding. * * Each letter-pair position carries $bitDepth bits. Guarding capacity up * front turns an unencodable payload into a clear caller-side error instead * of relying on the encoder's overflow exception alone. * * @param non-empty-string $payload The bytes to embed (already minimal). * @param positive-int $textLength The character count of the carrier text. * @param int<1, 2> $bitDepth Bits encoded per letter-pair position. * * @throws \OverflowException When the payload cannot fit the available positions. */function assertPayloadFits(string $payload, int $textLength, int $bitDepth): void{ $positions = $textLength - 1; $capacityBytes = \intdiv($positions * $bitDepth, 8);
if (\strlen($payload) > $capacityBytes) { throw new \OverflowException(\sprintf( 'Payload of %d bytes exceeds carrier capacity of %d bytes.', \strlen($payload), $capacityBytes, )); }}<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Enterprise\Security\Steganography\SteganographyDecoder;use NextPDF\Enterprise\Security\Steganography\SteganographyConfig;use NextPDF\Typography\FontMetrics;use Psr\Log\LoggerInterface;
final readonly class LeakTracer{ public function __construct(private LoggerInterface $logger) {}
/** * Recover the embedded marker from a suspect document's content stream. * * Decoding returns null on a wrong key or a destroyed carrier rather than * a wrong payload, so the caller treats null as "no marker recovered". * * @param string $contentStream The suspect content-stream bytes. * @param non-empty-string $fontKey The font key used at generation. * @param FontMetrics $metrics Font metrics with kerning pairs. * @param string $secretKey The same secret key used to encode. * @param SteganographyConfig $config The same configuration used to encode. * * @return string|null The recovered marker, or null when none is found. */ public function trace( string $contentStream, string $fontKey, FontMetrics $metrics, string $secretKey, SteganographyConfig $config, ): ?string { $marker = SteganographyDecoder::decodeFromContentStream( $contentStream, $fontKey, $metrics, $secretKey, $config, );
if ($marker === null) { $this->logger->info('No steganographic marker recovered from content stream.'); }
return $marker; }}- 將一個已知酬載編碼進一個已知文字 run,再以相同金鑰與設定將它解碼回來;確認還原出的酬載相符。
- 以一個故意錯誤的金鑰解碼,並確認結果為 null,而非一個錯誤的酬載——這就是 AEAD 完整性保證在發揮作用。
- 檢視呈現出的頁面,並確認在所設定的偏移比率下,間距變化在視覺上並不明顯。
- 當需要 PDF/A 相容時,對照你的 PDF/A 設定檔驗證輸出,並確認沒有觸發寬度容差。
安全與合規
標題為「安全與合規」的區段- 認證式加密。 酬載以 AES-256-GCM 或 ChaCha20-Poly1305 加密。一個被竄改或截斷的載體會在解密時通不過認證;它不會產出一個錯誤的酬載。
- 逐次加密的 IV。 每次加密都會產生一個全新隨機的 12 位元組 IV,滿足 AES-GCM 的唯一性要求,依 NIST SP 800-38D §5.2.1。
- 衍生金鑰。 加密金鑰以 HKDF-SHA-256 從你的密鑰與字型金鑰衍生而來(RFC 5869 §2)。請將密鑰保存在你的密碼管理器中;請像對待任何簽署密碼一樣對待它。
- 標記是文件內容。 嵌入的位元組是頁面內容的一部分,而非日誌內容。請勿將酬載或密鑰寫入日誌。
本頁涉及密碼學嵌入。每個規範性來源皆為改寫;不重現任何規範性文字。### 強健性揭露
標記承載於字距調整中。它可能被列印與重新掃描、被 PDF 轉換工具、被重新線性化,或被任何將字距正規化的內容串流改寫所破壞。此技術最適合用於以所產生形式散布之文件的內部洩漏追蹤。它不是對抗式隱寫術,也無法挺過任意的下游處理。當威脅模型包含蓄意剝除時,請勿將其作為唯一的控制措施而仰賴它。
失敗處理
標題為「失敗處理」的區段- 酬載過大。 當酬載超過文字容量時,編碼器會引發一個具型別的溢位錯誤。請縮短酬載或加長載體文字。
- 載體文字太少。 短於兩個字元的文字不提供任何載體位置,並會引發錯誤。
- 解碼時金鑰錯誤。 解碼回傳 null。請將 null 視為「無標記可還原」,而非部分結果。
- 設定不符。 編碼與解碼必須使用相同的位元深度、偏移比率、密碼與 PDF/A 旗標;不符會導致無法還原任何酬載。
發布邊界
標題為「發布邊界」的區段本頁僅記載外部可觀察的行為與受支援的公開 API 介面。內部命名空間路徑、輔助類別、機制表格、runbook 檔名,以及工單前綴皆不在範圍內。
另請參閱
標題為「另請參閱」的區段- Steganography — NextPDF Enterprise — 隱寫式編碼器與解碼器的 API 參考。
- Security — NextPDF Enterprise — 整合後的 Enterprise 安全介面。
- Branding — NextPDF Enterprise — 可見浮水印與頁面上的戳記。
- Forensics — NextPDF Enterprise — 文件檢驗與追蹤。
- Security — NextPDF Core — 核心加密與簽章介面。
- AEAD · HKDF · kerning — 詞彙表條目。