Technical Article

Revizija šifriranja in dovoljenj PDF v Delphiju s PDFlibPas

Zastavica dovoljenja ni varnostni mehanizem. Bit, ki pravi "brez kopiranja", živi znotraj istega slovarja /Encrypt kot kriptografija, kar mu daje pridih uveljavljanja, ki ga nima, in ko ti dve stvari obravnavate kot eno, začne vaša revizija dajati napačne odgovore. Edino vprašanje, ki si ga je vredno zastaviti glede PDF-ja, ni "ali je šifriran". Je bolj specifično in težje: kateri algoritem, katera revizija varnostnega predstavitvenega programa, katero od obeh gesel je bilo nastavljeno, kateri biti dovoljenj so zahtevani in katerih delov datoteke se šifriranje dejansko dotika. Datoteka je lahko formalno šifrirana in praktično odprta. Lahko zavrne branje, a pusti svoje metapodatke v navadnem besedilu. Tiskanje lahko zaklene z zastavico, ki jo vsak pregledovalnik lahko prezre. Revizija PDF pomeni ločeno reševanje vseh teh vprašanj, PDFlibPas, losLabov pogon PDF za Delphi in C++Builder, pa vsakega od njih izpostavi prek ravnega API-ja s celoštevilskimi ročaji in prek tipiziranega razrednega sloja.

Kaj slovar /Encrypt dejansko beleži

Standard ISO 32000-1 §7.6 definira varnost dokumenta prek peščice vnosov v slovarju, PDFlibPas pa jih preslika ena proti ena v zapis TPDFEncryption. Različica filtra V in revizija R izbereta družino algoritmov. Length prenaša velikost ključa. Biti dovoljenj se nahajajo v P, potrditveni nizi gesel lastnika in uporabnika v O in U (z dodanima OE in UE za AES-256), poleg tega je prisotna zastavica EncryptMetadata, tri dodatna polja pa poimenujejo kriptografske filtre, ki se uporabljajo za nize, tokove in vgrajene datoteke.

Vrednost tega zapisa je v tem, da ne interpretira ničesar namesto vas. Vrne vam surovi slovar in vam prepusti sklepanje, kar je natanko tisto, kar revizija potrebuje. Primer navadnega besedila znotraj šifriranega se pokaže v StringFilterIdentity in StreamFilterIdentity: ko je katera koli od teh vrednosti True, ustrezni podatki preidejo skozi filter Identity nedotaknjeni, ne glede na to, kaj poroča šifrirano stanje dokumenta. Optični bralnik, ki se ustavi pri "prisoten je slovar /Encrypt", bo takšno datoteko označil za zaščiteno, čeprav njeni nizi in tokovi ležijo v čistopisu. Enaka podrobnost velja za metapodatke. Ko je EncryptMetadata False, paket XMP ostane berljiv za kateri koli indeksirnik, medtem ko vsebina strani ni, kar je dobro vedeti v trenutku, ko vaša pravila usmerjanja temeljijo na polju naslova ali avtorja.

Kratka varnostna poizvedba z ravnim API-jem

Za večino cevovodov štirje ravni klici odgovorijo na vsakodnevna vprašanja. LoadFromFile vrne 1 ob uspehu, ko pa je dokument odprt, nadzorniki šifriranja poročajo o njegovem dešifriranem stanju:

var
  PDF: TPDFlib;
begin
  PDF := TPDFlib.Create;
  try
    if PDF.LoadFromFile('contract.pdf', UserPassword) <> 1 then
      raise Exception.Create('Open failed: wrong password or damaged file');
    Writeln('status    : ', PDF.EncryptionStatus);     // decrypted / encrypted / unknown
    Writeln('algorithm : ', PDF.EncryptionAlgorithm);  // RC4 vs AES family
    Writeln('strength  : ', PDF.EncryptionStrength);   // key length class
    Writeln('owner pw? : ', PDF.CheckPassword(CandidatePassword));
  finally
    PDF.Free;
  end;
end;

Metoda CheckPassword je pomembnejša, kot kaže njena enovrstična podpisna koda. PDF definira dve gesli z neenako močjo. Uporabniško geslo je potrebno za splošno odpiranje datoteke. Geslo lastnika podeli polne pravice in povozi vsak bit dovoljenj. Bajti na disku so v obeh primerih enaki, vendar lahko seja, odprta z geslom lastnika, počne stvari, ki jih seja z uporabniškim geslom ne more, zato revizija, ki ne zabeleži, katero poverilnico ste predložili, beleži le polovico resnice. Razredni sloj omogoča poizvedovanje o tej razliki. TPDFDocument.HasUserPassword in HasOwnerPassword poročata, kaj datoteka zahteva, medtem ko IsUserPassword in IsOwnerPassword poročata, katero geslo je dejansko odprlo trenutno sejo. Zabeležite to dejstvo. Nikoli ne beležite samih vrednosti gesel.

Lestvica moči, kjer "AES-256" pomeni dve stvari

Ravni funkciji Encrypt in EncryptFile sprejmeta celo število Strength s petimi pomenljivimi vrednostmi: 0 za 40-bitni RC4, 1 za 128-bitni RC4, 2 za 128-bitni AES (berljiv od različice Acrobat 7 naprej), 3 za 256-bitni AES (uveden z Acrobatom 9) in 4 za 256-bitni AES (zahtevan od Acrobata X naprej).

Zanimivo je, da sta tako 3 kot 4 označena kot AES-256, vendar ne gre za isto shemo. Moč 3 se preslika v revizijo 5 varnostnega predstavitvenega programa, začasno obliko, ki jo je izdal Acrobat 9 in je ISO ni nikoli sprejel. Moč 4 se preslika v revizijo 6, katere funkcija izpeljave ključa je bila utrjena in standardizirana v ISO 32000-2. Za dokument, ki ga ustvarjate danes, ni razloga, da bi izbrali 3 namesto 4. Za revizijo je ta vrzel odločilna: politiko, ki zahteva "AES-256 po ISO 32000-2", izpolnjuje le R6, datoteka R5, ki se predstavlja kot AES-256, pa tej politiki ne ustreza, čeprav prestane preprost test moči. Razredni sloj ju loči po imenih esAES256Bit za R5 proti esAES256BitAcroX za R6, lastnost EncryptionAcroX pa odgovori na vprašanje o reviziji z eno samo logično vrednostjo.

Biti dovoljenj in drobni tisk o dolžini ključa

Metoda EncodePermissions zapakira osem zastavic v celo število, ki ga prikakujeta Encrypt in EncryptFile. Tiskanje, kopiranje, spreminjanje in dodajanje opomb sestavljajo osnovni nabor; izpolnjevanje polj, kopiranje za dostopnost, sestavljanje in tiskanje v polni kakovosti pa sestavljajo razširjeni nabor. Drobni tisk, ki ga navaja že sam predstavitveni program za šifriranje v knjižnici, je, da razširjene štiri zastavice stopijo v veljavo šele pri moči 128 bitov in več. Zastavica za tiskanje v polni kakovosti sledi istemu pravilu: če jo počistite, da bi izsilili tiskanje z nizko ločljivostjo, vas bo 40-bitni dokument prezrl, saj ta znižanje kakovosti prav tako zahteva 128-bitno ali močnejše šifriranje. Če v 40-bitno datoteko kodirate politiko "le tiskanje z nizko ločljivostjo", bo vsak pregledovalnik kljub temu tiskal v polni kakovosti.

Globje vprašanje je, kdo sploh uveljavlja te bite, in odgovor je, da nihče, ki bi mu lahko zaupali. Dovoljenja so navodila za skladne bralnike in ne cryptographic omejitve. Dešifrirni ključ je enak, če je kopiranje dovoljeno ali zavrnjeno, so zaklenjen nabor dovoljenj ohranja le poštene uporabnike poštene. Bralnik, ki se odloči prezreti bite, ne naleti na nobeno kriptografsko oviro. Če je vaša obveznost preprečiti izbiro podatkov in ne le odsvetovati jo, datoteka potrebuje uporabniško geslo, delovni tok pa procesne kontrole, revizijsko poročilo pa bi moralo navesti, pod katerim režimom je datoteka dejansko, namesto da obravnava zastavico dovoljenja kot ključavnico.

Določanje politike in dokazovanje njene uveljavitve

Uporaba šifriranja na obstoječih datotekah ne zahteva njihovega nalaganja v objektno drevo. Metoda EncryptFile obdela vhod v izhod v enem klicu, revizijska zanka pa ponovno odpre rezultat, da potrdi, kaj je pristalo na disku. Priloženi predstavitveni program za šifriranje sledi enaki obliki zapisa in branja nazaj:

var
  PDF: TPDFlib;
  R: Integer;
begin
  PDF := TPDFlib.Create;
  try
    R := PDF.EncryptFile('in.pdf', 'out.pdf', 'owner-secret', 'user-secret', 4,
      PDF.EncodePermissions(1, 0, 0, 0,    // print allowed; copy/change/notes denied
                            0, 0, 0, 1));  // extended set: full-quality print only
    if (R = 1) and (PDF.LoadFromFile('out.pdf', 'user-secret') = 1) then
    begin
      Writeln('algorithm = ', PDF.EncryptionAlgorithm);
      Writeln('strength  = ', PDF.EncryptionStrength);
      Writeln('owner pw accepted: ', PDF.CheckPassword('owner-secret'));
    end;
  finally
    PDF.Free;
  end;
end;

Ekipe, ki delajo na ravni dokumentov, dobijo enako operacijo z uporabo tipiziranih naborov namesto pakiranja bitov, kar preživi pregled kode z veliko manj ugibanja:

if not Doc.Encrypt('owner-secret', 'user-secret', esAES256BitAcroX,
  [ppCanPrint], [ppCanPrintFull]) then
  raise Exception.Create('Encryption failed');

V vsakem primeru korak branja nazaj ni neobvezna ceremonija. Ujame namestitvene napake, ki se ose občasno pojavijo na strankinem računalniku: stara gradnja knjižnice, ki tiho zniža zahtevano moč, izhodna pot, ki nikoli ni bila zapisana zaradi pravic imenika, ali celo število dovoljenj, pri katerem so šli argumenti v napačnem vrstnem redu. Vse tri napake prestanejo lokalni hitri test in spodletijo na terenu, ponovno odpiranje izhoda pa vsako od njih spremeni v izjemo, ki jo vidite med izvajanjem. Metoda GetEncryptionFingerprint vrne kompaktno vrednost, ki jo lahko shranite z zapisom o opravilu, tako da lahko kasnejša primerjava ugotovi, ali si dva izhoda delita enako konfiguracijo šifriranja, ne da bi odpirali katero koli od njiju.

Lažno pozitivni rezultati revizije, ki jih je vredno kodirati

Nekaj vzorcev zanesljivo pripelje varnostne optične bralnike do napačnega zaključka, vsak od njih pa izhaja iz poenostavljanja večdelnega vprašanja v odgovor Da/Ne. Kriptografski filter Identity je najčistejši primer. Slovar /Encrypt je prisoten, datoteka se predstavlja kot šifrirana, kljub temu pa nizi in tokovi tečejo skozi filter Identity nespremenjeni, tako da je dejanska vsebina čistopis. Rešitev je branje StringFilterIdentity in StreamFilterIdentity pred označevanjem česar koli za zaščiteno.

Razdelitev metapodatkov je bolj subtilna. Zastavica EncryptMetadata se lahko razlikuje od preostalega dokumenta v obeh smereh, s čimer pusti šifrirano datoteko z berljivim paketom XMP ali pa obratno. Trditev "datoteka je šifrirana" ne pove ničesar o tem, ali so šifrirani njeni metapodatki, kar je pomembno takoj, ko indeksirnik ali pravilo usmerjanja potrebuje naslov. Vgrajene datoteke dodajajo tretjo os: PDF dovoljuje namenski šifrirni filter samo za priloge, tako da so priloge lahko edini šifrirani del sicer odprtega dokumenta ali pa edini nešifrirani del šifriranega. Zajemite tri dodelitve filtrov kot ločena polja za nize, tokove in vgrajene datoteke, pa vas nobena od teh pasti ne more ujeti. Shranite en sam boolean in napačna odločitev je le vprašanje časa.

Odstranjevanje šifriranja in izbira za nove datoteke

Revizija se pogosto konča z odločitvijo o odstranitvi zaščite, pri čemer mehanika ne predstavlja ovire. DecryptFile(InputFileName, OutputFileName, Password) zapiše dešifrirano kopijo brez celotnega nalaganja, metoda Decrypt na naloženem dokumentu pa stori enako v pomnilniku, ko je datoteka že odprta. Obe zahtevata veljavno geslo; nobena ne zaobide kriptografije. Prava ovira je politika in ne koda, zato v svojih pravilih jasno navedite, kdaj je odstranitev dovoljena, in zabeležite razred gesla, ki jo je odobril, saj tehnični korak sam po sebi ne pušča sledi.

Izbira za nov izhod je ožja, kot nakazuje pet vrednosti Strength. Uporabite Strength 4, AES-256 revizijo 6, razen če morate odpirati datoteke v pregledovalnikih, starejših od Acrobata X. Strength 2, AES-128, je pragmatično dno za starajočo se floto pregledovalnikov, ki je ni mogoče nadgraditi. Možnosti RC4 pri 0 in 1 sta namenjeni branju in reviziji zgodovinskih arhivov in ne ustvarjanju novih datotek; poseganje po njih pri načrtovanju leta 2026 je znak, da je zahteva zastarela.

Stanje šifriranja neposredno vpliva na odločitve o podpisovanju, saj delovna miza, ki validira in podpisuje dokumente, potrebuje enako disciplino branja nazaj, na katero se opira ta revizija. To področje pokriva članek Orodna vrstica za skladnost in podpisovanje. Ko paket uporabi EncryptFile na tisočih velikih dokumentih, priročnik Neposreden dostop do velikih PDF prikazuje, kako ohraniti nizko porabo pomnilnika. Celotna referenca API za šifriranje se nahaja na produktni strani PDFlibPas.