Teknisk artikkel

AES-256 PDF-kryptering i Delphi: HotPDF-oppsett og fallgruver

Et PDF-tillatelsesflagg er ingen lås. Det er en forespørsel filen retter til det som åpner den, og en viser står fritt til å ignorere den. Dette ene faktumet avgjør hvordan du bør resonnere om alle andre valg på denne siden. Ekte konfidensialitet kommer fra ett sted, og ett sted alene: AES-256-kryptering nøklet med et passord leseren ikke har. Alt annet, avkrysningsboksene «ingen utskrift» og «ingen kopiering», er policy som lovlydig programvare går med på å respektere, og som fiendtlig programvare ikke gjør. Bland sammen disse to lagene, og du sender fra deg noe som føles sikkert i en demo og lekker i felt

HotPDF er en nativ VCL PDF-komponent for Delphi og C++Builder, og den eksponerer ISO 32000s beskyttelsesmodell gjennom et lite sett med egenskaper. Egenskapene er enkle å sette. Den vanskelige delen er å vite hvilken som gir deg kryptografisk beskyttelse, og hvilken som bare gir deg en høflig anbefaling, samt å få tilordningsrekkefølgen riktig slik at krypteringen du ba om, faktisk er krypteringen du får

Hva de to passordene faktisk lover

PDF-kryptering definerer to legitimasjoner med ulike oppgaver, og å blande dem sammen er den vanligste designfeilen i kode som skal produsere beskyttet output. Brukerpassordet styrer dekrypteringen. Uten det, eller eierpassordet, kan en lovlydig leser ikke rekonstruere filnøkkelen, og innholdet forblir kryptografisk uleselig. Eierpassordet styrer i stedet tillatelsesinnstillingene: en leser som mottar eierpassordet, får full tilgang uansett hva restriksjonsflaggene sier

Tillatelsesbitene står på svakere grunn. Utskrift, innholdsuttrekk, skjemautfylling: hver av dem er et flagg en viser leser og velger å respektere (ISO 32000-2 §7.6.4). Kryptering beskytter bytene. Tillatelsesflaggene instruerer bare lovlydig programvare, og de gjør det i etterkant. Alle som åpner dokumentet med brukerpassordet, har allerede det dekrypterte innholdet i minnet, så «ingen kopiering» og «ingen utskrift» betyr noe for en veloppdragen viser og ingenting for en bestemt en. Bygg trusselmodellen rundt denne linjen. Konfidensialitet ligger i brukerpassordet. Tillatelser former hva gjengse visere tilbyr, og det er alt de gjør

HotPDF-diagram over krypterte PDF-legitimasjonsopplysninger der brukerpassordet utleder filnøkkelen og låser opp dekryptering, eierpassordet gir full tilgang som overstyrer tillatelsesflagg, og et advarselsbånd merker at ProtectOptions tillatelsesbiter er ønsker som bare æres av konformerende programvare
Brukerpassordet bærer konfidensialitet, mens eierpassordet bare løfter restriksjoner; tillatelsesflagg styrer samsvarsvillige lesere og binder ingen fiendtlig aktør

Konfigurasjonsrekkefølge: alt før BeginDoc

HotPDF bygger krypteringsdictionaryen og utleder filnøkkelen i det øyeblikket BeginDoc kjører. Det beskyttelsesegenskapene inneholder i akkurat det øyeblikket, er det dokumentet får, og å endre dem etterpå endrer ingenting. Egenskapen som betyr mest her, er CryptKeyLength, som velger krypteringsmetoden fra THPDFKeyType-verdiene k40, k128, aes128 og aes256. Tilordner du den etter BeginDoc, får du intet unntak, ingen advarsel, bare en fil som stille beholdt det den startet med. Denne typen stille avvik er den verste sorten: den består hver lokal test og dukker opp måneder senere som et samsvarsfunn på en kundes skrivebord

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'statement.pdf';
    Pdf.ActivateProtection := True;
    Pdf.CryptKeyLength := aes256;        // må settes før BeginDoc
    Pdf.UserPassword := 'open-secret';
    Pdf.OwnerPassword := 'admin-secret';
    Pdf.UseAES256R6 := False;            // R=5: bredest støtte blant visere
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 720, 0, 'Account statement, June 2026');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Passord er UTF-8 og har et tak på 127 byte, som er ISO 32000-2s grense for AES-256-metodene. Hvis passordpolicyen din gir deg lengre hemmeligheter, må du selv gjøre avkuttingen, på din side, der du kontrollerer nøyaktig hvor kuttet faller. Overlater du det til tilfeldighetene, kan biblioteket og en fremtidig viser bli uenige om avkuttingspunktet, noe som gir en fil som åpner seg for deg og avviser det samme passordet et annet sted

Revisjon 5 eller revisjon 6: én boolean, to økosystemer

UseAES256R6 velger mellom de to AES-256-håndtrykkene, og valget har større konsekvenser enn den boolske typen skulle tilsi. La den stå på False, så skriver HotPDF revisjon 5, AES-256-metoden som kom som en utvidelse til PDF 1.7, og som omtrent femten års visere kan åpne. Sett den til True, og du får revisjon 6, den herdede nøkkelutledningen standardisert i ISO 32000-2 for PDF 2.0, som lukker en kjent svakhet i hvordan revisjon 5 verifiserer passordet

Så revisjon 6 er kryptografisk den bedre historien. Det er også den som ødelegger ting. En revisjon 6-fil trenger en viser bygget for PDF 1.7 Extension Level 3 eller PDF 2.0, og mye distribuert programvare er verken det ene eller det andre: arkiver for saksbehandling, innebygde rendere i andre produkter, forretningsverktøy ingen har rørt på årevis. De vil avvise filen rett ut, og de vil gjøre det på kundens maskin, aldri på din. Det praktiske standardvalget er derfor revisjon 5. Grip til revisjon 6 bare når en sikkerhetspolicy navngir ISO 32000-2 etter revisjon, og når du faktisk har bekreftet at alle mottakere kan lese den. Uansett hva du velger, skriv ned hvilken du valgte og hvorfor, for neste person som rører denne koden, vil lure på det

De eldre nøkkeltypene fortjener en setning, så du vet at du kan hoppe over dem. THPDFKeyType lister fortsatt opp k40, k128 og aes128, men de finnes for å gjenskape historiske arkiver, ikke for å beskytte nye. 40-bit RC4 faller for vanlig maskinvare, og 128-bit-metodene er eldre enn de AES-256-revisjonene enhver dagens sikkerhetsgjennomgang vil forvente. For et dokument du lager i 2026, er det eneste reelle spørsmålet revisjon 5 mot revisjon 6; griper du til de utdaterte typene i et nytt design, har noe gått galt lenger oppe i kjeden

Tillatelsesflagg uten et åpent passord

Ofte er kravet det motsatte av hemmelighold. Alle skal kunne lese dokumentet, men utskrift eller uttrekk skal være begrenset. Du uttrykker det med et tomt brukerpassord og et ikke-tomt eierpassord, noe PDF kaller åpen-passord-modus, og du lister opp operasjonene du vil tillate i ProtectOptions

Side-om-side Delphi-flyter som viser at beskyttelsesegenskaper som CryptKeyLength aes256 tildelt før BeginDoc gir en AES-256-fil, mens samme tildeling etter BeginDoc lar den opprinnelige ordningen være urørt uten unntak eller advarsel
BeginDoc fryser krypteringsordboken, så riktige rekkefølge-spor gir den etterspurte AES-256-revisjonen, mens feil rekkefølge-spor i stillhet sender ut standardskjemaet
Pdf.ActivateProtection := True;
Pdf.CryptKeyLength := aes256;
Pdf.UserPassword := '';                      // hvem som helst kan åpne filen
Pdf.OwnerPassword := 'rotate-me-quarterly';  // beskytter tillatelsessettet
Pdf.ProtectOptions := [prPrint, prPrint12bit, prExtractContent];
Pdf.BeginDoc;
// ... sideinnhold ...
Pdf.EndDoc;

Settet THPDFProtectOptions tilsvarer ISOs tillatelsesbiter: prPrint og prPrint12bit for høyoppløselig utskrift, prInformationCopy for generell kopiering og uttrekk, prExtractContent for uttrekk til hjelpeteknologi, i tillegg til prModifyStructure, prEditAnnotations, prFillAnnotations og prAssemble. To av dem fortjener en advarsel. La prExtractContent stå på i nesten hver profil du bygger. Det er biten en skjermleser trenger for å nå teksten, og å fjerne den gjør stille en rettighetsbeslutning om til en tilgjengelighetsdefekt som noen med en funksjonsnedsettelse støter på, og som du aldri ser. Den andre fellen er prPrint alene, uten prPrint12bit: flere visere reagerer med å forringe utskriftskvaliteten, og brukerne dine vil melde det som en rendringsfeil snarere enn den tillatelsesinnstillingen det faktisk er

Verifisering tar fem minutter og hører hjemme i utgivelsessjekklisten din. Åpne et utvalg av hver profil i Acrobat, åpne Dokumentegenskaper, og les Sikkerhet-fanen, som staver ut algoritmen («AES 256-bit») og lister opp de tillatte operasjonene én etter én. Åpne så den samme filen i den eldste viseren kundene dine faktisk kjører, ikke den nyeste på din egen maskin. Denne andre åpningen er den billige forsikringen mot at en revisjon 6-fil seiler gjennom utviklingen og dør hos en kunde som aldri oppgraderte

Å fjerne beskyttelse fra eksisterende filer

Dekryptering kjører den samme egenskapsmodellen baklengs. Last inn dokumentet med en gyldig legitimasjon, slå av beskyttelsen, og lagre resultatet uten den

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;   // fjern kryptering ved lagring
      Pdf.SaveLoadedDocument('plain.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Denne fremgangsmåten parser hele dokumentet inn i minnet, noe som er greit for vanlige filer og sløsete for enorme. Når inndataen strekker seg over hundrevis av megabyte, er DecryptFile det billigere alternativet: det dekrypterer under en kopiering på filnivå, og tar en direkte AES-256-omskrivingsvei som hopper over å bygge det fullstendige objekttreet når inndataen tillater det. Dette er en del av Direct File API-et som dekkes i følgeartikkelen om å behandle store PDF-er fra Delphi

Begrensninger som samspiller med kryptering

To begrensninger er verdt å kjenne til før du designer rundt kryptering, snarere enn etterpå. Den første er arkivsamsvar. ISO 19005 forbyr kryptering i PDF/A, så enhver arbeidsflyt som krypterer et dokument og samtidig hevder PDF/A-samsvar, er motstridende av natur; HotPDF lar deg ikke ha begge deler i én fil. Når du virkelig trenger begge deler, er svaret to artefakter: en kryptert kopi til distribusjon og en separat ukryptert kopi til arkivet

Den andre begrensningen er strengere. PDF-kryptering har verken deponering eller gjenoppretting. Mister du brukerpassordet på en R5- eller R6-fil, er alternativene dine brute force eller å gi opp. Behandle derfor eier- og brukerhemmeligheter på samme måte som du behandler enhver produksjonslegitimasjon. Generer dem, lagre dem i et hvelv, og roter dem etter en fast plan. Det ene du aldri må gjøre, er å hardkode dem som konstanter i en enhet, der de går rett inn i versjonskontrollen og blir liggende i hver utviklers arbeidskopi for alltid

Én siste refleks er verdt å bygge inn. Å endre beskyttelsen på en fil du ikke selv opprettet, er det samme maskineriet som dekryptering, ikke en egen funksjon: last den inn med passordet gjennom LoadFromFile, rediger ProtectOptions eller passordene på stedet, og skriv den tilbake med SaveLoadedDocument. Kan du dekryptere en fil, kan du også omfordele tillatelsene i den, og koden ser nesten identisk ut som eksempelet ovenfor

Beskyttelsesegenskapene som vises her, er en del av standard HotPDF Delphi Component for Delphi og C++Builder; produktsiden inneholder den fullstendige krypteringsreferansen, inkludert den komplette tillatelsesenumereringen