Przejdź do głównej zawartości
getnextpdf.com

Enterprise edycja

Cyfrowy znak wodny i osadzanie steganograficzne

NextPDF Enterprise osadza ukryty, zaszyfrowany ładunek w generowanym pliku PDF, wprowadzając niewielkie, kontrolowane korekty odstępu między parami liter. Dostarczasz ładunek — zwykle identyfikator dla danego odbiorcy — oraz tajny klucz; koder zapisuje ładunek jako niezauważalne odchylenia od naturalnego kerningu tekstu. Pasujący dekoder, dysponując tym samym kluczem, odzyskuje ładunek. Ta strona jest na poziomie zachowania: przedstawia, co zapisuje koder, jakiej kryptografii używa oraz jaka jest granica techniki.

Zamierzonym zastosowaniem jest wewnętrzne śledzenie wycieku dokumentów: gdy kontrolowany dokument wycieknie, odzyskany znacznik identyfikuje kopię odbiorcy. To nie jest steganografia adwersaryjna ani gwarancja przetrwania dowolnego przetwarzania.

Wymagania wstępne są podane we frontmaterii i powtórzone w sekcji Wymagania wstępne.

Ta możliwość jest dostarczana w NextPDF Enterprise (nextpdf/enterprise) i aktywuje się wraz z kopertą licencyjną poziomu Enterprise. Wdrożenie bez tego uprawnienia nie ładuje klas tej możliwości. Możliwość działa w całości w procesie podczas generowania dokumentu; żadna treść dokumentu nie opuszcza hosta. Porównaj edycje i uzyskaj licencję.

Tekst PDF rysowany z tablicą kerningu niesie liczbową korektę między glifami. Koder traktuje każdą pozycję korekty jako nośnik kilku bitów:

  1. Szyfruje ładunek szyfrem ze szyfrowaniem uwierzytelnionym z danymi powiązanymi (AEAD) — domyślnie AES-256-GCM lub ChaCha20-Poly1305. AEAD zapewnia zarówno poufność, jak i integralność, więc zmanipulowany nośnik kończy niepowodzeniem deszyfrowania, zamiast po cichu zwracać błędny ładunek.
  2. Wyprowadza 32-bajtowy klucz szyfrowania z twojego tajnego klucza oraz klucza czcionki za pomocą funkcji wyprowadzania klucza opartej na HMAC (HKDF) z SHA-256. HKDF wyodrębnia pseudolosowy klucz o stałej długości z wejściowego materiału kluczowego, a następnie rozszerza go do wymaganej długości, zgodnie z RFC 5869 §2.
  3. Generuje świeży losowy 12-bajtowy wektor inicjujący (IV) na każde szyfrowanie. AES-GCM wymaga, aby IV był unikalny dla danego klucza, w przeciwnym razie gwarancja uwierzytelnienia jest tracona, zgodnie z NIST SP 800-38D §5.2.1.
  4. Odwzorowuje zaszyfrowane bajty na sekwencję bitów i rozkłada bity na dostępnych pozycjach par liter, kodując jeden lub dwa bity na pozycję. Odchylenie, które dodaje do naturalnego kerningu, jest ograniczone konfigurowalnym ułamkiem firetu (em) — na tyle małym, by pozostać wizualnie niezauważalnym.

Dekoder odwraca proces: odczytuje korekty kerningu ze strumienia treści, odejmuje naturalny kerning, kwantyzuje odchylenia z powrotem do bitów, ponownie składa zaszyfrowany blok i odszyfrowuje go tym samym kluczem. Jeśli klucz jest błędny lub nośnik został zniszczony, deszyfrowanie zwraca brak, zamiast błędnego ładunku.

Pojemność skaluje się wraz z długością tekstu: każda pozycja pary liter niesie jeden lub dwa bity, więc ładunek musi zmieścić się w pozycjach, które dostarcza tekst. Koder zgłasza typowany błąd przepełnienia, gdy ładunek przekracza pojemność.

Tryb zgodności z PDF/A zmniejsza o połowę maksymalne odchylenie, aby pozostać poniżej progu tolerancji szerokości walidatora, wymieniając pojemność na ściślejszą zgodność.

Kluczowym wyborem jest ukrycie znacznika w kerningu, a nie w widocznej nakładce lub polu metadanych. Znacznik w metadanych jest trywialny do usunięcia, a widoczny stempel zmienia stronę. Odchylenia kerningu zamiast tego jadą wewnątrz tekstu, który odbiorca musi zachować, i pozostają niezauważalne. Szyfrowanie uwierzytelnione to drugi filar: zmanipulowany lub częściowy nośnik nie przechodzi uwierzytelnienia, więc dekoder zwraca nic, zamiast błędnego odbiorcy. Klucz jest wyprowadzany dla każdej czcionki za pomocą HKDF, wiążąc znacznik z kontekstem dokumentu, a nie z gołym współdzielonym sekretem. Uczciwe ujawnienie odporności wynika z tego wprost: znacznik przetrwa zwykłą redystrybucję, lecz nie celowe przepisanie strumienia treści, więc podany zakres to wewnętrzne śledzenie wycieku, a nie steganografia adwersaryjna.

Tło projektowe: Redakcja to nie czarny prostokąt.

  1. Zainstaluj NextPDF Core oraz pakiet Enterprise i posiadaj aktywną licencję Enterprise.
  2. Wygeneruj dokument z czcionką, która udostępnia metryki par kerningu; koder odczytuje naturalny kerning z metryk czcionki.
  3. Dostarcz tajny klucz z menedżera sekretów, a nie ze źródła. Ten sam klucz jest wymagany do dekodowania.
  4. Zdecyduj o głębi bitowej (jeden lub dwa bity na pozycję) oraz o tym, czy wymagana jest zgodność z PDF/A, w oparciu o twoje potrzeby pojemności i zgodności.

Konfiguracja kodowania jest niezmienna i walidowana podczas konstrukcji:

  • Głębia bitowa — jeden lub dwa bity na pozycję pary liter. Większa głębia daje więcej pojemności, lecz większe odchylenia.
  • Maksymalny współczynnik korekty — pułap odchylenia jako ułamek firetu (em), w ograniczonym zakresie. Większe wartości dają więcej zapasu, lecz grożą widocznością.
  • Szyfr — AES-256-GCM (domyślny) lub ChaCha20-Poly1305. Oba to AEAD.
  • Zgodność z PDF/A — gdy włączona, zmniejsza o połowę efektywne maksymalne odchylenie.

Użyj tej samej konfiguracji do kodowania i dekodowania; niezgodność daje brak odzyskanego ładunku.

  1. Odczytaj tajny klucz z menedżera sekretów.
  2. Zbuduj konfigurację kodowania (głębia bitowa, współczynnik odchylenia, szyfr, flaga PDF/A).
  3. Oblicz korekty kerningu dla tekstu, który masz zamiar wyrenderować, przekazując ładunek, tekst, klucz czcionki, metryki czcionki, tajny klucz oraz konfigurację.
  4. Zastosuj zwrócone korekty podczas zapisywania ciągu tekstu, aby znacznik został osadzony podczas generowania.
  5. Aby prześledzić kopię, która wyciekła, uruchom dekoder nad strumieniem treści podejrzanego dokumentu z tym samym kluczem czcionki, metrykami czcionki, tajnym kluczem oraz konfiguracją i odczytaj odzyskany ładunek.
examples/enterprise/steganography-capacity-guard.php
<?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,
));
}
}
examples/enterprise/steganography-decode-guarded.php
<?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;
}
}
  1. Zakoduj znany ładunek w znanym ciągu tekstu, a następnie odkoduj go z powrotem tym samym kluczem i konfiguracją; potwierdź, że odzyskany ładunek się zgadza.
  2. Odkoduj celowo błędnym kluczem i potwierdź, że wynikiem jest null, a nie błędny ładunek — to gwarancja integralności AEAD w działaniu.
  3. Sprawdź wyrenderowaną stronę i potwierdź, że zmiana odstępu nie jest wizualnie zauważalna przy skonfigurowanym współczynniku odchylenia.
  4. Gdy wymagana jest zgodność z PDF/A, zwaliduj wynik względem swojego profilu PDF/A i potwierdź, że tolerancja szerokości nie została naruszona.
  • Szyfrowanie uwierzytelnione. Ładunek jest szyfrowany za pomocą AES-256-GCM lub ChaCha20-Poly1305. Zmanipulowany lub obcięty nośnik kończy niepowodzeniem uwierzytelnienia podczas deszyfrowania; nie zwraca błędnego ładunku.
  • IV na szyfrowanie. Świeży losowy 12-bajtowy IV jest generowany dla każdego szyfrowania, spełniając wymóg unikalności AES-GCM zgodnie z NIST SP 800-38D §5.2.1.
  • Wyprowadzony klucz. Klucz szyfrowania jest wyprowadzany za pomocą HKDF-SHA-256 z twojego sekretu oraz klucza czcionki (RFC 5869 §2). Trzymaj sekret w menedżerze sekretów; traktuj go jak każdy sekret podpisujący.
  • Znacznik to treść dokumentu. Osadzone bajty są częścią treści strony, a nie treści logu. Nie zapisuj ładunku ani tajnego klucza do logów.

Ta strona dotyczy kryptograficznego osadzania. Każde źródło normatywne jest parafrazowane; nie odtwarza się żadnego tekstu normatywnego. ### Ujawnienie odporności

Znacznik jest niesiony w korektach kerningu. Może zostać zniszczony przez drukowanie i ponowne skanowanie, przez narzędzia konwersji PDF, przez ponowną linearyzację lub przez dowolne przepisanie strumienia treści, które normalizuje kerning. Technika najlepiej nadaje się do wewnętrznego śledzenia wycieku dokumentów dystrybuowanych w postaci wygenerowanej. To nie jest steganografia adwersaryjna i nie przetrwa dowolnego przetwarzania pochodnego. Nie polegaj na niej jako jedynym mechanizmie kontroli tam, gdzie model zagrożeń obejmuje celowe usuwanie.

  • Ładunek zbyt duży. Koder zgłasza typowany błąd przepełnienia, gdy ładunek przekracza pojemność tekstu. Skróć ładunek lub wydłuż tekst nośnika.
  • Zbyt mało tekstu nośnika. Tekst krótszy niż dwa znaki nie oferuje żadnej pozycji nośnika i zgłasza błąd.
  • Błędny klucz przy dekodowaniu. Dekodowanie zwraca null. Traktuj null jako „brak odzyskanego znacznika”, a nie jako wynik częściowy.
  • Niezgodność konfiguracji. Kodowanie i dekodowanie muszą używać tej samej głębi bitowej, współczynnika odchylenia, szyfru oraz flagi PDF/A; niezgodność daje brak odzyskanego ładunku.

Ta strona dokumentuje wyłącznie zewnętrznie obserwowalne zachowanie oraz wspieraną publiczną powierzchnię API. Wewnętrzne ścieżki przestrzeni nazw, klasy pomocnicze, tabele mechanizmów, nazwy plików runbooków oraz prefiksy zgłoszeń są poza zakresem.