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

Podpisywanie na dużą skalę, bez kompromisów

Spec: ISO 32000-2, §12.8Spec: ETSI EN 319 142-1Spec: RFC 5652, §5.1

Podpisanie jednego dokumentu to operacja kryptograficzna. Podpisanie stu tysięcy na czas to ta sama operacja powtórzona, w której groźną awarią nie jest już „było wolno”, lecz „jeden z nich wyszedł niepodpisany i nikt tego nie zauważył”. Ta strona jest o robieniu drugiej rzeczy bez rezygnacji z pierwszej: masowego i równoległego podpisywania, w którym każdy podpis jest nadal poprawny, przebieg odmawia wypuszczenia pliku, którego nie potrafił podpisać, a duże zadanie wznawia się zamiast zaczynać od nowa.

Podpis to fakt na poziomie dokumentu. Jego skrót jest obliczany dla zadeklarowanego zakresu bajtów, który pomija samą wartość podpisu (Spec: ISO 32000-2, §12.8), więc nie ma uczciwego sposobu, by podpisać tysiąc dokumentów „jako partię” jednym ruchem — każdy niesie własny obiekt CMS SignedData dla własnych bajtów (Spec: RFC 5652, §5.1). Skala zatem zwielokrotnia szanse, że dokładnie jedna rzecz po cichu pójdzie nie tak: uchwyt do klucza, który chwilowo zawiódł, urząd znaczników czasu, który przekroczył limit czasu, proces roboczy, który padł, trzymając na wpół zapisany plik.

Kosztownym wynikiem nie jest awaria. Awaria jest głośna i ponawiasz ją. Kosztownym wynikiem jest ten cichy — niepodpisany PDF, który wygląda na ukończony, leżący w archiwum, odkryty miesiące później przez walidator audytora. Na wolumenie „w większości podpisane” jest nie do odróżnienia od „podpisane” aż do momentu, gdy sprawdza się ten jeden, który ma znaczenie. Całym sensem podpisywania na dużą skalę jest uczynienie tego wyniku strukturalnie niemożliwym, a nie statystycznie rzadkim.

  • Każdy dokument jest podpisywany indywidualnie, dla własnego zakresu bajtów. Partia to słowo z dziedziny harmonogramowania, nie kryptografii. Nie ma wspólnego podpisu.
  • Poziom jest kontraktem, nie wskazówką. Nazywasz poziom bazowy PAdES, a silnik wytwarza dokładnie ten poziom dla każdego dokumentu albo głośno zawodzi na tym dokumencie (Spec: ETSI EN 319 142-1).
  • Potok jest fail-closed. Dokument, którego nie da się poprawnie podpisać, nie przechodzi dalej jako zwykłe bajty. Jest zatrzymywany, a nie przekazywany.
  • Równoległość działa per dokument i jest bezpieczna z konstrukcji. Jednostki podpisujące nie współdzielą zmiennego stanu, więc dwa procesy robocze nie mogą uszkodzić swoich wzajemnych wyjść.
  • Duże przebiegi są trwałe. Zatwierdzone wyjście nie jest emitowane ponownie przy wznowieniu; przebieg, który padł, kontynuuje od ostatniego punktu kontrolnego zamiast podpisywać wszystko od nowa.

Projekt opiera się na jednym podziale: wytworzenie podpisu to mały, deterministyczny krok per dokument; bezpieczne uruchomienie ich tysięcy to krok orkiestracji. Trzymanie tych dwóch rzeczy osobno pozwala każdej z nich pozostać prostą.

Krok podpisu to ten, który nigdy nie może iść na kompromis. Prosisz o poziom — przypadek enumeracji SignatureLevel, nigdy ciąg znaków, który silnik musi interpretować — a ten poziom jest traktowany jak kontrakt dla tego dokumentu. Silnik wytwarza żądany poziom albo zatrzymuje się z czytelnym błędem; nie podpisuje po cichu na niższym poziomie, pozwalając zapisowi twierdzić, że jest wyższy. Poprawność nie luzuje się, bo za tym jest więcej dokumentów. Stutysięczny podpis jest obliczany dokładnie tak starannie jak pierwszy.

Reguła fail-closed sprawia, że to jest godne zaufania na wolumenie. Ścieżka podpisywania NextPDF odmawia wyemitowania wyglądającego wiarygodnie, lecz niepodpisanego artefaktu w miejsce tego, o który prosiłeś. Wspieraną drogą aplikacyjną jest wysokopoziomowe API Document: konfigurujesz podpis przez Document::setSignature(), a następnie prosisz o bajty przez Document::getPdfData() (lub save() / output()), a ten pojedynczy przebieg zapisu albo emituje poprawnie podpisany PDF, albo rzuca wyjątek przed oddaniem bajtów — nigdy nie niepodpisany plik, który wywołujący uważa za podpisany. Zastosowana w partii reguła ta zamienia „jeden prześlizgnął się niepodpisany” z cichego utajonego defektu w pojedyncze nieudane, ponawialne zadanie.

  1. Warm the signing material onceOn worker boot, open the key/certificate source and the timestamp client. This cost is paid once per worker, not once per document.
  2. Enqueue the documentsA queue holds the per-document jobs. The queue is the throughput dial — signing workers scale horizontally behind it.
  3. Render and sign one documentA disposable unit renders the document, then signs it over its own byte range at the requested PAdES level. Nothing is shared with the next document.
  4. Commit on success, hold on failureA correctly-signed file commits once. A document that could not be signed is failed and retried — never emitted as unsigned bytes.
  5. Checkpoint, and resume on crashA durable run records what has committed. After a crash it continues from the last checkpoint instead of re-signing the whole batch.
Przebieg podpisywania na dużą skalę od początku do końca: wspólny materiał podpisujący jest rozgrzewany raz; każdy dokument jest renderowany i podpisywany indywidualnie na jednostce jednorazowej; poprawnie podpisany wynik zatwierdza się dokładnie raz, a każda awaria jest zatrzymywana do ponowienia, nigdy nie przekazana dalej jako niepodpisane bajty; przebieg, który padł, wznawia się z punktu kontrolnego.

Core daje Ci kryptograficzną poprawność: programowe podpisywanie CMS oraz PAdES B-B (z B-T przez klienta znaczników czasu), gdzie każdy dokument jest podpisywany indywidualnie i fail-closed. Orkiestracja, która czyni duży przebieg trwałym, równoległym i exactly-once — silnik renderujący bez efektów ubocznych plus komiter, punkt kontrolny, idempotencja i magazyny martwych listów — to moduł Stream w edycjach zaawansowanych; podpisywanie wsparte sprzętowo przez HSM lub chmurowy KMS jest podobnie szwem edycji zaawansowanych. Core dowodzi, że każdy podpis jest poprawny; edycje zaawansowane sprawiają, że milion z nich da się przeżyć.

Kształt poniżej to jednostka podpisująca per dokument wewnątrz pętli partii. Każda iteracja podpisuje jeden dokument na nazwanym poziomie i albo daje poprawnie podpisany wynik, albo zawodzi to jedno zadanie — nigdy nie zwraca niepodpisanych bajtów przebranych za wynik.

<?php
declare(strict_types=1);
use NextPDF\Contracts\DocumentFactoryInterface;
use NextPDF\Security\Signature\CertificateInfo;
use NextPDF\Security\Signature\SignatureLevel;
use NextPDF\Exception\SignatureException;
use Psr\Log\LoggerInterface;
/**
* One signing-batch iteration: render, sign at a named level, commit or fail.
*
* The factory and the certificate source ($certInfo, the warmed signing
* material) are process-lifetime singletons; the document is disposable. A
* document that cannot be signed at the requested level fails this job loudly —
* it is never committed unsigned.
*
* @param iterable<int, callable(\NextPDF\Core\Document): \NextPDF\Core\Document> $jobs
*/
function signBatch(
DocumentFactoryInterface $factory,
CertificateInfo $certInfo,
LoggerInterface $logger,
iterable $jobs,
): void {
// The level is an explicit, ordered contract — not a flag we hope is honoured.
$level = SignatureLevel::PAdES_B_T;
foreach ($jobs as $jobId => $build) {
// Fresh, disposable unit — shares the warmed signing material only.
$doc = $factory->create();
$doc = $build($doc);
try {
// Sign over this document's own byte range, at exactly $level,
// or throw. There is no "signed lower, reported higher" path.
$doc->setSignature(certInfo: $certInfo, level: $level);
$signed = $doc->getPdfData();
} catch (SignatureException $e) {
// Fail-closed: this document does NOT continue as unsigned bytes.
// The job is failed and left for retry / dead-letter handling.
$logger->error('pdf.sign.failed', ['job_id' => $jobId, 'reason' => $e->getMessage()]);
continue;
}
// Only a correctly-signed result reaches the commit step.
commitSignedOutput($jobId, $signed);
unset($doc, $signed); // release per-document state before the next iteration
$logger->info('pdf.sign.committed', ['job_id' => $jobId, 'level' => $level->value]);
}
}

catch to wiersz nośny. To różnica między przebiegiem, który zatrzymuje dokumenty, których nie potrafił podpisać, a przebiegiem, który wypuszcza je mimo to. continue nie zamiata awarii pod dywan — zadanie jest odnotowane i pozostawione do ponowienia, więc partia kończy się ze znaną, kompletną listą tego, co się podpisało, i co się nie podpisało, nigdy z cichą luką.

Pierwsze nieporozumienie głosi, że „podpisywanie wsadowe” oznacza jeden podpis nałożony na wiele plików. Nie oznacza, a każdy system, który tak twierdzi, nie wytwarza prawidłowych podpisów PAdES — skrót każdego dokumentu jest związany z jego własnymi bajtami (Spec: ISO 32000-2, §12.8). Partia dotyczy wyłącznie ilu i jak szybko, nigdy współdzielenia jednostki kryptograficznej.

Drugie głosi, że równoległość oznacza luzowanie poprawności na rzecz szybkości — że szybki podpisujący musi ściąć róg, którego staranny nie ścina. Nie musi. Ponieważ jednostki podpisujące nie współdzielą zmiennego stanu, uruchamianie ich równolegle zmienia harmonogram, a nie bajty. Każdy równoległy podpis jest obliczany z tą samą rygorystycznością co pojedynczy; równoległość jest w orkiestracji wokół nich.

Trzecie głosi, że trwałość to coś, co doczepiasz po pierwszym nieudanym przebiegu nocnym. Wtedy przebieg jest już stracony. Wznawialny potok musi wiedzieć, per dokument, co się zatwierdziło, a co nie, przed awarią — a to dokładnie po to istnieją magazyny punktów kontrolnych i idempotencji.

  • Każdy podpis jest per dokument i związany ze standardem; nie ma skrótu na partię. Wolumen zmienia harmonogramowanie, nie jednostkę kryptograficzną. NextPDF podpisuje każdy dokument dla jego własnego zakresu bajtów.
  • Core robi programowe podpisywanie CMS oraz PAdES B-B (B-T przez klienta znaczników czasu). Trwały, równoległy, exactly-once silnik renderowania i podpisywania to moduł Stream w edycjach zaawansowanych; sprzętowo wsparte przechowanie kluczy HSM/KMS jest szwem edycji zaawansowanych. Ta strona nie zalicza tej orkiestracji do Core.
  • Fail-closed to zachowanie silnika, a nie gwarancja co do Twojego okablowania. NextPDF odmawia wyemitowania pliku niepodpisanego-lecz-uważanego-za-podpisany i wystawia wspieraną drogę podpisywania. Potok, który łapie wynikowy błąd i zatwierdza mimo to, wybrał pokonanie gwarancji — to ujęcie, któremu przykładowe catch/continue ma zapobiegać.
  • Poziom PAdES jest egzekwowany per dokument, a nie certyfikowany dla przebiegu. Silnik wytwarza żądany poziom bazowy albo zawodzi; to strukturalne egzekwowanie, a nie werdykt zgodności strony trzeciej dla wytworzonych plików. Sama progresja poziomów jest omówiona w Profile bazowe PAdES.
  • Kolejka, przechowanie kluczy, urząd znaczników czasu i magazyn obiektów są Twoje. NextPDF dostarcza poprawność podpisywania per dokument oraz, w edycjach zaawansowanych, trwałe prymitywy orkiestracji. Nie prowadzi Twojej infrastruktury ani nie ręczy za Twój urząd TSA.
High-volume and concurrent signing — edition availability
EditionAvailability
Core

Programowe podpisywanie CMS per dokument, PAdES B-B (B-T z klientem znaczników czasu), podpisywanie indywidualne dla własnego zakresu bajtów każdego dokumentu, fail-closed wobec po cichu niepodpisanego wyjścia. Zwykłe podpisywanie per dokument nie wymaga edycji komercyjnej.

Pro

Dodaje moduł Stream: silnik renderujący bez efektów ubocznych plus trwały komiter, punkt kontrolny, idempotencję i magazyny martwych listów — równoległe, odporne na awarie, exactly-once przebiegi wsadowe, które wznawiają się zamiast zaczynać od nowa.

Enterprise

Dodaje sprzętowo wsparte przechowanie kluczy (HSM przez PKCS#11 lub chmurowy KMS), aby klucz prywatny nigdy nie opuszczał urządzenia, oraz długoterminowe poziomy PAdES (B-LT, B-LTA), które utrzymują archiwum na dużą skalę weryfikowalnym przez dekady.

  • Generowanie dokumentów na dużą skalę — model partii oparty na kolejce i ograniczonej pamięci, na którym ta strona podpisuje; przeczytaj go najpierw, by poznać dyscyplinę przepustowości i pomiaru.
  • Profile bazowe PAdES — co dodaje każdy poziom (od B-B do B-LTA), abyś podpisywał na poziomie, którego wymaga zobowiązanie.
  • Jak podpisy są osadzone w pliku PDF — fundament zakresu bajtów i słownika, który czyni podpis per dokument.
  • Podpisywanie wsparte HSM — gdzie leży granica klucza prywatnego, gdy materiał podpisujący żyje w sprzęcie.
  • Stream (Pro) — trwały, równoległy, exactly-once silnik renderujący, który zamienia pojedynczą jednostkę podpisującą w wznawialny przebieg.
  • Podpisywanie wsadowe — podpisywanie wielu dokumentów zgodnie z harmonogramem. Koncept harmonogramowania; każdy dokument jest nadal podpisywany indywidualnie dla własnych bajtów.
  • Fail-closed — przy awarii, która w przeciwnym razie wytworzyłaby niepodpisane lub błędne wyjście, potok zatrzymuje dokument i raportuje, zamiast przekazywać go dalej jako zwykłe bajty.
  • Zatwierdzenie exactly-once — właściwość trwałego potoku, w której poprawnie podpisane wyjście jest publikowane raz i nie jest emitowane ponownie, gdy przebieg, który padł, wznawia się.
  • Punkt kontrolny — trwały zapis per dokument tego, co się zatwierdziło, aby przebieg mógł kontynuować od miejsca, w którym się zatrzymał, zamiast podpisywać wszystko od nowa.
  • CMS SignedData — kryptograficzny kontener na podpisy nad treścią (może nieść wielu podpisujących); ten potok wytwarza podpis PDF jednego podpisującego per dokument, jednostkę per dokument, którą wytwarza partia.
  • PAdES — PDF Advanced Electronic Signatures, rodzina profili ETSI EN 319 142 do podpisywania PDF; jej poziomy bazowe biegną od B-B do B-LTA.