Uzmite račun u PDF-u koji već ima AES-256 šifriranje i zatražite od PDFium Componenta za Delphi i C++Builder (PDFiumPas) da ga označi kao PDF/A radi arhiviranja ili da ga potpiše standardom PAdES putem inkrementalnog ažuriranja umjesto potpunog ponovnog zapisivanja. Biblioteka to neće postići izravnim krpanjem šifriranih bajtova: njezinih šest ubrizgivača oznaka sukladnosti prepoznaje postojeći unos /Encrypt i prosljeđuje izvor bajt po bajt neizmijenjen, a potpisnik PAdES-a podiže iznimku umjesto da izda potpis koji nijedan validator neće prihvatiti
To je drukčije pitanje od provjere PDF-a koji niste izradili radi skrivenih rizika, što je zaseban postupak samo za čitanje. Ovaj se članak bavi stranom zapisivanja iste granice povjerenja: što vaš kod smije učiniti s datotekom čiji su bajtovi već zaključani tuđom lozinkom u trenutku kada joj taj kod pokuša naknadno nešto dodati
Što ISO 32000-1 zahtijeva pri ažuriranju šifriranog PDF-a?
ISO 32000-1 §7.5.6 zahtijeva da trailer inkrementalnog ažuriranja ponovi svaki unos iz prethodnog trailera osim /Prev, a Tablica 15 navodi /Encrypt među unosima koje trailer može sadržavati. Izostavite ga iz novog trailera i usklađeni čitač nema razloga posumnjati u izostavljanje: najnoviji trailer je mjerodavan, pa čitač koji ondje ne pronađe /Encrypt zaključi da cijela datoteka nije šifrirana i pokuša starije, još šifrirano tijelo raščlaniti kao obične bajtove. Zadržite /Encrypt u novom traileru, ali objekte samog ažuriranja zapišite kao običan tekst, i pogreška se samo pomiče korak kasnije: čitač ispravno prepoznaje šifriranje, provlači svaki objekt koji dotakne kroz šifru datoteke, uključujući nove objekte koji nikad nisu bili šifrirani, pa za sadržaj koji je prije dešifriranja bio savršeno čitljiv dobiva šum. Obje pogreške stvaraju datoteku koja na razini bajtova izgleda kao normalno, dobro oblikovano inkrementalno ažuriranje sve dok je usklađeni čitač ne otvori
Šest ubrizgivača oznaka i jedna šifrirna zaštita u v2.14.2
PDFiumPas isporučuje šest ubrizgivača oznaka na razini bajtova, po jedan za svaki ISO podskup PDF-a koji može označiti: PDF/A (ISO 19005), PDF/X (ISO 15930), PDF/UA (ISO 14289-1), PDF/E-1 (ISO 24517-1), PDF/R-1 (ISO 23504-1) i PDF/VT-1 (ISO 16612-2). Svaki uzima bajtove koje je PDFium već zapisao vlastitim FPDF_SaveAsCopy i preko njih slaže drugo, manje inkrementalno ažuriranje: novi XMP tok metapodataka, izmjenu kataloškog rječnika koja pokazuje na njega te za podskupove usmjerene na ispis OutputIntent i ICC profil. Od v2.14.2 svaki od InjectPdfAMarkers, InjectPdfXMarkers, InjectPdfUaMarkers, InjectPdfEMarkers, InjectPdfRMarkers i InjectPdfVTMarkers najprije čita izvorni trailer, a ako prijavi postojeći unos /Encrypt, neizmijenjen kopira izvor u odredišni tok i odmah se vraća. Nema XMP-a, nema OutputIntenta ni izmjene kataloga — pozivatelj dobiva izvornu datoteku natrag, bajt po bajt
var
Src, Dst: TFileStream;
Opts: TPdfXSaveOptions;
begin
Src := TFileStream.Create('signed-encrypted-proof.pdf', fmOpenRead);
Dst := TFileStream.Create('pdfx-attempt.pdf', fmCreate);
try
Opts.Conformance := pxc4;
InjectPdfXMarkers(Src, Dst, Opts);
// pdfx-attempt.pdf is byte-identical to the source: still encrypted,
// no /GTS_PDFXVersion, no OutputIntent. Nothing was written, and
// nothing was corrupted either
finally
Dst.Free;
Src.Free;
end;
end;
Dopušteno šifriranje ne znači da je sigurno ubrizgavati u datoteku
PDF/E-1 i PDF/R-1 izričito dopuštaju da njihov matični dokument bude šifriran na razini specifikacije, što izgleda kao izuzeće dok ne pogledate što se zapravo mora dogoditi na disku. ISO 24517-1 §6.3 dopušta šifriranje za PDF/E-1, a ISO 23504-1 §6.2.3 dopušta ga za PDF/R-1 pod uvjetom da zaglavlje navodi %PDF-2.0. Nijedna odredba ne govori može li naknadni procesor na razini bajtova sigurno dodati običan tekstualni objekt u taj šifrirani spremnik, a ne može, iz istih razloga prema §7.5.6 koji vrijede za svaki drugi podskup. Vlastiti validatori usklađenosti PDFiumPasa za ta dva profila, ValidatePdfECompliance i ValidatePdfRCompliance, namjerno bilježe prisutnost /Encrypt bez označavanja nedostatka, što je ispravno za validator samo za čitanje koji ne zapisuje nijedan bajt. Lako je to previdjeti i pretpostaviti da susjednom ubrizgivaču ne treba zasebna zaštita, iako je upravo ubrizgivač ona funkcija iz para koja mora odbiti postupak
Dešifrira li SaveAsPdfX tiho vaš dokument?
Da, kad koristite javne pomoćne metode umjesto izravnog poziva ubrizgivača. Svaka od metoda TPdf.SaveAsPdfA, SaveAsPdfX, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfR i SaveAsPdfVT iscrtava trenutni dokument u privremeni tok pomoću SaveAs(Tmp, saRemoveSecurity) prije predaje tih bajtova odgovarajućem ubrizgivaču. saRemoveSecurity preslikava se na vlastitu PDFiumovu oznaku FPDF_REMOVE_SECURITY, pa privremena kopija koju ubrizgivač primi od početka nije bila šifrirana i zaštita ubrizgivača za /Encrypt nema se zbog čega aktivirati. Izlaz sadrži vaše oznake PDF/A, PDF/X, PDF/UA, PDF/E-1, PDF/R-1 ili PDF/VT-1, ali više nije zaštićen lozinkom kojom je izvor otvoren
Ta je zamjena nevidljiva sve dok netko nizvodno ne otvori „zaštićenu” arhivsku kopiju bez lozinke i primijeti da radi bez problema. Rješenje nije drugi poziv metode; PDFiumPas nema pandan saAddSecurity za saRemoveSecurity, jer temeljni PDFiumov stroj nikad nije bio izgrađen za zapisivanje novog šifriranja, nego samo za njegovo uklanjanje. Ako su oba svojstva važna za jednu datoteku, šifriranje mora biti zaseban korak koji vi kontrolirate i koji se primjenjuje nakon oznaka sukladnosti, a ne unutar istog poziva SaveAsPdfA
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.Password := 'open-secret'; // needed to open the source at all
Pdf.FileName := 'signed-encrypted-invoice.pdf';
Pdf.LoadDocument;
Pdf.SaveAsPdfA('invoice-pdfa.pdf', pac2b);
// invoice-pdfa.pdf now declares PDF/A-2b, but SaveAs(saRemoveSecurity)
// ran first inside SaveAsPdfA: the output opens without a password
finally
Pdf.Free;
end;
end;
Što se događa kada šifrirani PDF potpišete standardom PAdES?
PDFiumPas postupak odmah odbija, umjesto da zahtjev nečujno ispusti kao ubrizgivač oznaka. TPdf.SignPades i SignPadesToStream oba vode kroz interni SignPadesBytes, a prvo što on učini nakon raščlambe izvornog trailera jest provjera /Encrypt. Ako je unos prisutan, podiže EPadesCrypto s porukom "SignPadesBytes: the source document is encrypted; remove encryption before signing" umjesto da nastavi. InjectPadesDssMarkers, funkcija koja umeće certifikate, OCSP odgovore i CRL-ove za dugoročnu provjeru, primjenjuje istu provjeru iz istog razloga, s vlastitom porukom: "InjectPadesDssMarkers: the source document is encrypted; remove encryption before embedding DSS validation material"
Ovdje je obrazloženje strože nego kod prosljeđivanja ubrizgivača oznaka, i to namjerno. Nečujno prosljeđivanje sigurno je za oznaku PDF/A jer preskakanje ostavlja isti valjani PDF koji ste započeli, samo bez oznake. Potpis ne smije tako tiho zakazati: potpis koji nikad nije dodan izgleda svakom pozivajućem kodu koji provjerava samo Booleovu vrijednost potpuno jednako kao uspješno dodan potpis. EPadesCrypto nasljeđuje običnu klasu Exception, pa je njezino hvatanje uobičajena obrada iznimki, a ne posebna konvencija upravljanja tokom koju morate učiti
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.Password := 'open-secret';
Pdf.FileName := 'encrypted-contract.pdf';
Pdf.LoadDocument;
try
Pdf.SignPades('encrypted-contract-signed.pdf', 'A1B2C3D4E5F6...');
except
on E: EPadesCrypto do
// E.Message: 'SignPadesBytes: the source document is encrypted;
// remove encryption before signing'
raise Exception.Create('Decrypt the contract before signing: '+ E.Message);
end;
finally
Pdf.Free;
end;
end;
Redoslijed oznaka sukladnosti, potpisa i šifriranja
Praktično je rješenje redoslijed, a ne druga biblioteka. Najprije primijenite oznake PDF/A, PDF/X, PDF/UA, PDF/E-1, PDF/R-1 ili PDF/VT-1, zatim dodajte potpis PAdES, a tek onda pokrenite korak u svojem cjevovodu koji stvarno upravlja šifriranjem, bilo da je to namjenski pisač PDF-a, uređaj za potpisivanje ili vaša vlastita implementacija AES-a. Sloj inkrementalnog ažuriranja PDFiumPasa prirodno se uklapa u sredinu tog redoslijeda, dodajući male, ciljane objekte na inače dovršenu datoteku, a šifriranje pripada na kraj upravo zato što je to jedina operacija u lancu koju PDFiumPas sam ne može izvesti ni poništiti
Ništa od toga ne mijenja način na koji PDFiumPas čita trailer i podatke unakrsnih referenci o kojima ovisi svako inkrementalno ažuriranje, što postaje zaseban izvor suptilnosti kada se pojave tokovi xref; članak provjera objektnih i xref tokova u PDF-u opisuje kako ista putanja čitanja trailera obrađuje komprimirane strukture PDF-a 1.5+. A kada je dokument spreman za nešto snažnije od oznake sukladnosti, potpisivanje PDF-a potpisom PAdES B-B u Delphiju mjesto je na kojem SignPades preuzima postupak točno ondje gdje ovaj članak završava
Ovdje opisani ubrizgivači oznaka i metode SignPades isporučuju se kao dio proizvoda PDFium Component za Delphi i C++Builder, uz iscrtavanje i pregled samo za čitanje koje PDFium izvorno pruža