Teknisk artikel

AES-256 PDF-kryptering i Delphi: HotPDF-opsætning og faldgruber

Et PDF-tilladelsesflag er ikke en lås. Det er en anmodning, filen retter til, hvad end der åbner den, og en viser står frit for at ignorere det. Netop det faktum afgør, hvordan man bør ræsonnere om alle andre valg på denne side. Reel fortrolighed kommer kun fra ét sted: AES-256-kryptering med en nøgle afledt af en adgangskode, som læseren ikke har. Alt andet, afkrydsningsfelterne "ingen udskrivning" og "ingen kopiering", er politik, som konform software accepterer at overholde, og som fjendtlig software ikke gør. Bland de to lag sammen, og man leverer noget, der føles sikkert i en demo og lækker i felten

HotPDF er en native VCL PDF-komponent til Delphi og C++Builder, og den eksponerer ISO 32000-beskyttelsesmodellen gennem et lille sæt egenskaber. Egenskaberne er lette at sætte. Det svære er at vide, hvilken der giver kryptografisk beskyttelse, og hvilken der blot giver et høfligt forslag, samt at få tildelingsrækkefølgen rigtig, så den kryptering, man har bedt om, rent faktisk er den kryptering, man får

Hvad de to adgangskoder rent faktisk lover

PDF-kryptering definerer to legitimationsoplysninger med hver sin opgave, og at sammenblande dem er den mest almindelige designfejl i kode, der producerer beskyttet output. Brugeradgangskoden styrer dekrypteringen. Uden den, eller ejeradgangskoden, kan en konform læser ikke genskabe filnøglen, og indholdet forbliver kryptografisk ulæseligt. Ejeradgangskoden styrer i stedet tilladelsesindstillingerne: en læser, der modtager ejeradgangskoden, får fuld adgang, uanset hvad begrænsningsflagene siger

Tilladelsesbittene står på svagere grund. Udskrivning, indholdsudtræk, formularudfyldning: hver af dem er et flag, en viser læser og vælger at respektere (ISO 32000-2 §7.6.4). Kryptering beskytter bytes. Tilladelsesflagene instruerer kun konform software, og de gør det efterfølgende. Enhver, der åbner dokumentet med brugeradgangskoden, har allerede det dekrypterede indhold i hukommelsen, så "ingen kopiering" og "ingen udskrivning" betyder noget for en velopdragen viser og intet for en beslutsom en. Byg trusselsmodellen op omkring den linje. Fortroligheden bor i brugeradgangskoden. Tilladelser former, hvad de gængse visere tilbyder, og det er alt, de gør

HotPDF-diagram over krypterede PDF-credentials, hvor user password udleder filnøglen og låser dekryptering op, owner password giver fuld adgang og tilsidesætter permission flags, og en advarselsbjælke noterer, at ProtectOptions permission bits er anmodninger, som kun conforming software honorerer
Brugeradgangskoden bærer fortrolighed, mens ejeradgangskoden blot ophæver begrænsninger; tilladelsesflag styrer compliant-fremvisere og binder ingen fjendtlig

Konfigurationsrækkefølge: alt før BeginDoc

HotPDF opbygger krypterings-dictionaryen og udleder filnøglen i det øjeblik, BeginDoc kører. Hvad end beskyttelsesegenskaberne indeholder på det tidspunkt, er det, dokumentet får, og at ændre dem bagefter ændrer intet. Den egenskab, der betyder mest her, er CryptKeyLength, som vælger skemaet blandt THPDFKeyType-værdierne k40, k128, aes128 og aes256. Tildel den efter BeginDoc, og man får ingen undtagelse, ingen advarsel, blot en fil, der stille beholdt, hvad den startede med. Den slags stille afvigelse er den værste sort: den består hver eneste lokale test og dukker op måneder senere som et compliance-fund på en kundes skrivebord

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'statement.pdf';
    Pdf.ActivateProtection := True;
    Pdf.CryptKeyLength := aes256;        // skal sættes før BeginDoc
    Pdf.UserPassword := 'open-secret';
    Pdf.OwnerPassword := 'admin-secret';
    Pdf.UseAES256R6 := False;            // R=5: bredeste viewer-understøttelse
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 720, 0, 'Account statement, June 2026');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Adgangskoder er UTF-8 og maksimeret til 127 byte, hvilket er ISO 32000-2-grænsen for AES-256-skemaerne. Hvis din adgangskodepolitik giver dig længere hemmeligheder, så udfør afkortningen selv, på din side, hvor du har kontrol over præcis, hvor snittet lægges. Overlad det til tilfældet, og biblioteket og en fremtidig viser kan være uenige om afskæringen, hvilket giver en fil, der åbner hos dig og afviser den samme adgangskode et andet sted

Revision 5 eller revision 6: én boolean, to økosystemer

UseAES256R6 vælger mellem de to AES-256-håndtryk, og valget har større konsekvenser, end dets boolean-type antyder. Lad den stå på False, og HotPDF skriver revision 5, det AES-256-skema, der kom som en udvidelse til PDF 1.7, og som noget i retning af femten års visere kan åbne. Sæt den til True, og man får revision 6, den hærdede nøgleudledning standardiseret i ISO 32000-2 til PDF 2.0, som lukker en kendt svaghed i, hvordan revision 5 verificerer adgangskoden

Så revision 6 er den kryptografisk bedre historie. Den er også den, der ødelægger ting. En revision 6-fil kræver en viser bygget til PDF 1.7 Extension Level 3 eller PDF 2.0, og en stor del af den software, der er i drift, er hverken det ene eller det andet: records-management-arkiver, indlejrede renderere i andre produkter, forretningsværktøjer, ingen har rørt i årevis. De vil afvise filen fuldstændigt, og de vil gøre det på kundens maskine, aldrig på din. Det praktiske standardvalg er derfor revision 5. Ræk kun efter revision 6, når en sikkerhedspolitik nævner ISO 32000-2 ved revision, og når du rent faktisk har bekræftet, at enhver modtager kan læse den. Under alle omstændigheder, skriv ned, hvilken du valgte og hvorfor, for den næste person, der rører ved denne kode, vil undre sig

De ældre nøgletyper fortjener en sætning, så du ved, at de skal springes over. THPDFKeyType opremser stadig k40, k128 og aes128, men de findes for at gengive historiske arkiver, ikke for at beskytte nye. 40-bit RC4 falder for standardhardware, og 128-bit-skemaerne går forud for de AES-256-revisioner, som enhver nutidig sikkerhedsgennemgang vil forvente. For et dokument, du opretter i 2026, er det reelle spørgsmål kun revision 5 versus revision 6; hvis du finder dig selv ræk efter de gamle typer i et nyt design, er der gået noget galt længere oppe i kæden

Tilladelsesflag uden en åben adgangskode

Ofte er kravet det modsatte af hemmeligholdelse. Alle skal kunne læse dokumentet, men udskrivning eller udtræk skal begrænses. Det udtrykker man med en tom brugeradgangskode og en ikke-tom ejeradgangskode, hvilket PDF kalder open-password-tilstand, og man opremser de operationer, man vil tillade, i ProtectOptions

Side-om-side Delphi-flows, der viser protection-egenskaber som CryptKeyLength aes256 tildelt før BeginDoc giver en AES-256-fil, mens samme tildeling efter BeginDoc lader det oprindelige skema urørt uden exception eller advarsel
BeginDoc fryser krypteringsordbogen, så den rigtige rækkefølge-spor giver den ønskede AES-256-revision, mens det forkerte rækkefølge-spor lydløst sender standardskemaet
Pdf.ActivateProtection := True;
Pdf.CryptKeyLength := aes256;
Pdf.UserPassword := '';                      // alle kan åbne filen
Pdf.OwnerPassword := 'rotate-me-quarterly';  // beskytter tilladelsessættet
Pdf.ProtectOptions := [prPrint, prPrint12bit, prExtractContent];
Pdf.BeginDoc;
// ... sideindhold ...
Pdf.EndDoc;

Sættet THPDFProtectOptions afbilder direkte på ISO-tilladelsesbittene: prPrint og prPrint12bit til udskrivning i høj opløsning, prInformationCopy til generel kopiering og udtræk, prExtractContent til udtræk til hjælpeteknologi, plus prModifyStructure, prEditAnnotations, prFillAnnotations og prAssemble. To af dem fortjener en advarsel. Lad prExtractContent stå slået til i næsten enhver profil, du bygger. Det er det bit, en skærmlæser har brug for for at nå teksten, og at slå det fra omdanner stille og roligt en rettighedsbeslutning til en tilgængelighedsfejl, som en person med et handicap rammer ind i, og som du aldrig ser. Den anden fælde er prPrint alene, uden prPrint12bit: adskillige visere reagerer ved at forringe udskriftskvaliteten, og dine brugere vil indberette det som en gengivelsesfejl frem for den tilladelsesindstilling, det faktisk er

Verifikation tager fem minutter og hører hjemme i din release-tjekliste. Åbn et eksempel af hver profil i Acrobat, åbn Dokumentegenskaber, og læs fanen Sikkerhed, som staver algoritmen ud ("AES 256-bit") og lister de tilladte operationer én ad gangen. Åbn derefter den samme fil i den ældste viser, dine kunder rent faktisk kører, ikke den nyeste på din egen maskine. Den anden åbning er den billige forsikring mod, at en revision 6-fil sejler gennem udviklingen og dør hos en kunde, der aldrig opgraderede

Fjernelse af beskyttelse fra eksisterende filer

Dekryptering kører den samme egenskabsmodel baglæns. Indlæs dokumentet med en gyldig legitimation, slå beskyttelsen fra, og gem resultatet uden 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 gemning
      Pdf.SaveLoadedDocument('plain.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Den rute parser hele dokumentet ind i hukommelsen, hvilket er fint for almindelige filer og spild af ressourcer for enorme. Når input løber op i hundredvis af megabyte, er DecryptFile den billigere mulighed: den dekrypterer under en kopiering på filniveau og tager en direkte AES-256-omskrivningssti, der springer opbygningen af det fulde objekttræ over, når input tillader det. Det er en del af Direct File API'et, der dækkes i følgeartiklen om at behandle store PDF'er fra Delphi

Begrænsninger, der spiller sammen med kryptering

To grænser er værd at kende, før man designer omkring kryptering frem for bagefter. Den første er arkivkonformitet. ISO 19005 forbyder kryptering i PDF/A, så enhver workflow, der krypterer et dokument og samtidig hævder PDF/A-konformitet, er selvmodsigende i sin konstruktion; HotPDF vil ikke lade dig have begge dele i én fil. Når man reelt har brug for begge dele, er svaret to artefakter: en krypteret kopi til distribution og en separat ukrypteret kopi til arkivet

Den anden grænse er hårdere. PDF-kryptering har ingen deponering og ingen gendannelse. Mister man brugeradgangskoden til en R5- eller R6-fil, er ens muligheder brute force eller at give op. Behandl derfor ejer- og brugerhemmeligheder på samme måde, som man behandler enhver produktionslegitimation. Generér dem, opbevar dem i et vault, rotér dem efter en plan. Det ene, man aldrig må gøre, er at hardkode dem som konstanter i en unit, hvor de kører lige ind i versionsstyringen og sidder i enhver udviklers arbejdskopi for evigt

Én sidste refleks er værd at opbygge. At ændre beskyttelsen på en fil, man ikke selv har oprettet, er det samme maskineri som dekryptering, ikke en separat funktion: indlæs den med dens adgangskode via LoadFromFile, redigér ProtectOptions eller adgangskoderne på stedet, og skriv den tilbage med SaveLoadedDocument. Kan man dekryptere en fil, kan man også ændre dens tilladelser, og koden ser næsten identisk ud med eksemplet ovenfor

Beskyttelsesegenskaberne, der er vist her, er en del af den almindelige HotPDF Delphi Component til Delphi og C++Builder; produktsiden indeholder den fulde krypteringsreference, herunder den komplette opremsning af tilladelser