Technical Article

AES-256 PDF enkripcija u Delphi-ju: Podešavanje i zamke u HotPDF-u

Zastavica za dozvole (permission flag) u PDF-u nije brava. To je zahtev koji fajl postavlja pred bilo šta što ga otvori, a čitač je slobodan da ga ignoriše. Ta jednostavna činjenica određuje kako treba da razmišljate o svakom drugom izboru na ovoj stranici. Prava poverljivost dolazi samo sa jednog mesta: AES-256 enkripcije zasnovane na lozinki koju čitač nema. Sve ostalo, poput kućica za potvrdu „zabranjeno štampanje“ i „zabranjeno kopiranje“, jeste pravilo koje usaglašen softver pristaje da poštuje, a neprijateljski softver ne. Pomestajte ta dva nivoa i poslaćete nešto što deluje bezbedno u demo verziji, a curi na terenu.

HotPDF je izvorna VCL PDF komponenta za Delphi i C++Builder, i ona izlaže ISO 32000 zaštitni model kroz mali skup svojstava. Svojstva se lako podešavaju. Teži deo je znati koje od njih vam kupuje kriptografsku zaštitu, a koje pristojan predlog, kao i postavljanje ispravnog redosleda dodeljivanja kako bi enkripcija koju ste tražili bila upravo ona enkripcija koju ste i dobili.

Šta dve lozinke zapravo obećavaju

PDF enkripcija definiše dva kredencijala sa različitim ulogama, a njihovo mešanje je najčešća greška u dizajnu koda za zaštićeni izlaz. Korisnička lozinka (user password) kontroliše dešifrovanje. Bez nje ili lozinke vlasnika (owner password), usaglašeni čitač ne može rekonstruisati ključ fajla i sadržaj ostaje kriptografski nečitljiv. Lozinka vlasnika, s druge strane, kontroliše podešavanja dozvola: čitaču koji dobije lozinku vlasnika odobren je potpun pristup bez obzira na to šta zastavice ograničenja kažu.

Bitovi dozvola stoje na slabijim temeljima. Štampanje, izvlačenje sadržaja, popunjavanje obrazaca: svako je zastavica koju čitač čita i bira da li će je poštovati (ISO 32000-2 §7.6.4). Enkripcija štiti bajtove. Zastavice dozvola samo daju instrukcije usaglašenom softveru, i to nakon što se pristup već ostvari. Svako ko otvori dokument pomoću korisničke lozinke već drži dešifrovani sadržaj u memoriji, tako da „zabranjeno kopiranje“ i „zabranjeno štampanje“ znače nešto dobroćudnom čitaču, a ništa upornom napadaču. Izgradite model pretnji oko te linije. Poverljivost živi u korisničkoj lozinki. Dozvole oblikuju ono što uobičajeni čitači nude, i to je sve što rade.

Redosled konfiguracije: sve pre BeginDoc poziva

HotPDF gradi rečnik enkripcije i izvodi ključ fajla u trenutku kada se BeginDoc pokrene. Šta god da svojstva zaštite sadrže u tom trenutku jeste ono što dokument dobija, a njihova kasnija izmena ne menja ništa. Svojstvo koje je ovde najvažnije je CryptKeyLength, koje bira šemu iz vrednosti THPDFKeyType: k40, k128, aes128 i aes256. Dodelite ga nakon poziva BeginDoc i nećete dobiti nikakav izuzetak niti upozorenje, samo fajl koji je tiho zadržao ono sa čime je i počeo. Ta vrsta tihog odstupanja je najgora: prolazi svaki lokalni test, a pojavljuje se mesecima kasnije kao nalaz neusaglašenosti na stolu klijenta.

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'statement.pdf';
    Pdf.ActivateProtection := True;
    Pdf.CryptKeyLength := aes256;        // must be set before BeginDoc
    Pdf.UserPassword := 'open-secret';
    Pdf.OwnerPassword := 'admin-secret';
    Pdf.UseAES256R6 := False;            // R=5: widest viewer support
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 720, 0, 'Account statement, June 2026');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Lozinke su u UTF-8 formatu i ograničene su na 127 bajtova, što je ISO 32000-2 limit za AES-256 šeme. Ako vam vaša politika lozinki daje duže tajne, sami uradite skraćivanje (truncation), na svojoj strani, gde tačno kontrolišete gde rez pada. Prepustite to slučaju i biblioteka i neki budući čitač mogu se ne slagati oko mesta sečenja, što proizvodi fajl koji se otvara kod vas, a odbija istu lozinku na drugom mestu.

Revizija 5 ili revizija 6: jedan boolean, dva ekosistema

UseAES256R6 bira između dva načina povezivanja (handshakes) za AES-256, a taj izbor donosi veće posledice nego što to sugeriše njegov boolean tip. Ostavite ga na False i HotPDF upisuje reviziju 5, šemu AES-256 koja je stigla kao proširenje za PDF 1.7 i koju čitači stari oko petnaest godina mogu da otvore. Postavite ga na True i dobićete reviziju 6, pojačano izvođenje ključa standardizovano u ISO 32000-2 za PDF 2.0, koje zatvara poznatu slabost u načinu na koji revizija 5 verifikuje lozinku.

Dakle, revizija 6 je kriptografski bolja priča. Ali ona je i ona koja kvari stvari. Za fajl revizije 6 potreban je čitač napravljen za PDF 1.7 Extension Level 3 ili PDF 2.0, a mnogo instaliranog softvera nije ni jedno ni drugo: arhive za upravljanje zapisima, ugrađeni rendereri u drugim proizvodima, poslovni alati koje niko nije dirao godinama. Oni će odmah odbiti fajl, i to na računaru klijenta, nikada na vašem. Praktičan podrazumevani izbor je stoga revizija 5. Posegnite za revizijom 6 samo kada bezbednosna politika eksplicitno imenuje ISO 32000-2 po reviziji i kada ste zaista potvrdili da je svaki primalac može pročitati. U svakom slučaju, zapišite šta ste izabrali i zašto, jer će se sledeća osoba koja bude radila na ovom kodu pitati.

Stariji tipovi ključeva zaslužuju rečenicu kako biste znali da ih preskočite. THPDFKeyType i dalje navodi k40, k128 i aes128, ali oni postoje radi reprodukcije istorijskih arhiva, a ne radi zaštite novih. 40-bitni RC4 lako pada pred komercijalnim hardverom, a 128-bitne šeme su starije od revizija AES-256 koje će očekivati bilo koji trenutni bezbednosni pregled. Za dokument koji kreirate u 2026. godini, stvarno pitanje je samo revizija 5 naspram revizije 6; ako se nađete u situaciji da posežete za starim tipovima u novom dizajnu, nešto ranije u toku posla je pošlo naopako.

Zastavice dozvola bez lozinke za otvaranje

Često je zahtev suprotan od tajnosti. Svako bi trebalo da može da pročita dokument, ali štampanje ili izvlačenje sadržaja treba da budu ograničeni. To izražavate praznom korisničkom lozinkom i ne-praznom lozinkom vlasnika, što PDF naziva režimom otvorene lozinke (open-password mode), i navodite operacije koje želite da dozvolite u ProtectOptions.

Pdf.ActivateProtection := True;
Pdf.CryptKeyLength := aes256;
Pdf.UserPassword := '';                      // anyone can open the file
Pdf.OwnerPassword := 'rotate-me-quarterly';  // guards the permission set
Pdf.ProtectOptions := [prPrint, prPrint12bit, prExtractContent];
Pdf.BeginDoc;
// ... page content ...
Pdf.EndDoc;

Skup THPDFProtectOptions se mapira na ISO bitove dozvola: prPrint i prPrint12bit za štampanje visoke rezolucije, prInformationCopy za opšte kopiranje i izvlačenje sadržaja, prExtractContent za izvlačenje pomoću asistivne tehnologije, plus prModifyStructure, prEditAnnotations, prFillAnnotations i prAssemble. Dva od njih zaslužuju upozorenje. Ostavite prExtractContent uključenim u skoro svakom profilu koji kreirate. To je bit koji je čitaču ekrana potreban da bi pristupio tekstu, a njegovo isključivanje tiho pretvara odluku o pravima pristupa u problem pristupačnosti (accessibility defect) na koji nailazi osoba sa invaliditetom, a vi to nikada ne vidite. Druga zamka je prPrint sam za sebe, bez prPrint12bit: nekoliko čitača reaguje tako što smanjuje kvalitet štampe, a vaši korisnici će to prijaviti kao grešku u renderovanju, a ne kao podešavanje dozvole što zapravo jeste.

Verifikacija traje pet minuta i mesto joj je na vašoj kontrolnoj listi za objavljivanje. Otvorite uzorak svakog profila u Acrobat-u, otvorite Document Properties i pročitajte karticu Security, koja navodi algoritam („AES 256-bit“) i izlistava dozvoljene operacije jednu po jednu. Zatim otvorite isti fajl u najstarijem čitaču koji vaši kupci zapravo koriste, a ne u najnovijem na vašem računaru. To drugo otvaranje je jeftino osiguranje da fajl sa revizijom 6 neće proći kroz razvojni ciklus i prestati da radi kod klijenta koji nikada nije nadogradio softver.

Uklanjanje zaštite sa postojećih fajlova

Dešifrovanje pokreće isti model svojstava unazad. Učitate dokument sa važećim kredencijalom, isključite zaštitu i sačuvate rezultat bez nje.

var
  Pdf: THotPDF;
  PageCount: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    PageCount := Pdf.LoadFromFile('encrypted.pdf', 'open-secret');
    if PageCount > 0 then
    begin
      Pdf.ActivateProtection := False;   // drop encryption on save
      Pdf.SaveLoadedDocument('plain.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Taj put učitava ceo dokument u memoriju, što je u redu za obične fajlove, ali rasipno za one ogromne. Kada se ulazni podaci mere stotinama megabajta, metoda DecryptFile je jeftinija opcija: ona vrši dešifrovanje tokom kopiranja na nivou fajla, koristeći direktnu putanju ponovnog pisanja kroz AES-256 koja preskače izgradnju celog stabla objekata kad god ulazni podaci to dozvoljavaju. To je deo Direct File API-ja koji je pokriven u pratećem članku o obradi velikih PDF dokumenata u Delphi-ju.

Ograničenja koja utiču na enkripciju

Dva ograničenja vredi znati pre nego što dizajnirate sistem oko enkripcije, a ne nakon toga. Prvo je usaglašenost za arhiviranje. Standard ISO 19005 zabranjuje enkripciju u PDF/A formatu, tako da je svaki tok posla koji šifruje dokument i istovremeno tvrdi da je usaglašen sa PDF/A kontradiktoran po konstrukciji; HotPDF vam neće dozvoliti da imate oba u jednom fajlu. Kada su vam zaista potrebna oba, rešenje su dva dokumenta: šifrovana kopija za distribuciju i posebna nešifrovana kopija za arhivu.

Drugo ograničenje je još oštrije. PDF enkripcija nema mogućnost povraćaja (no recovery) ili posredovanja (no escrow). Izgubite korisničku lozinku na R5 ili R6 fajlu i vaše opcije su gruba sila (brute force) ili odustajanje. Zato tretirajte lozinke vlasnika i korisnika na način na koji tretirate bilo koji produkcioni kredencijal. Generišite ih, čuvajte ih u sefu i rotirajte po rasporedu. Jedina stvar koju nikada ne smete uraditi je da ih čvrsto kodirate (hard-code) kao konstante u kodu, gde odlaze direktno u sistem za kontrolu verzija i zauvek ostaju u radnoj kopiji svakog programera.

Još jedan refleks koji vredi izgraditi. Promena zaštite na fajlu koji niste vi kreirali koristi isti mehanizam kao i dešifrovanje, a ne neku posebnu funkciju: učitate ga sa njegovom lozinkom kroz LoadFromFile, izmenite ProtectOptions ili same lozinke, i upišete ga nazad pomoću SaveLoadedDocument. Ako možete da dešifrujete fajl, možete mu i promeniti dozvole, a kod izgleda skoro identično kao u gornjem primeru.

Svojstva zaštite prikazana ovde deo su standardne HotPDF komponente za Delphi i C++Builder; stranica proizvoda sadrži kompletnu referencu za enkripciju, uključujući celokupno nabrajanje dozvola.