Technisch artikel

AES-256 PDF-versleuteling in Delphi: HotPDF en valkuilen

Een PDF-toestemmingsvlag is geen slot. Het is een verzoek dat het bestand doet aan wat het ook maar opent, en een viewer mag het negeren. Dat ene feit bepaalt hoe u over elke andere keuze op deze pagina moet redeneren. Echte vertrouwelijkheid komt maar uit één bron: AES-256-versleuteling met een wachtwoord dat de lezer niet heeft. Al het andere, de selectievakjes "niet afdrukken" en "niet kopiëren", is beleid dat conforme software belooft te honoreren en vijandige software niet. Verwissel die twee lagen en u levert iets af dat in een demo veilig aanvoelt en in de praktijk lekt

HotPDF is een native VCL PDF-component voor Delphi en C++Builder, en het stelt het ISO 32000-beschermingsmodel beschikbaar via een klein aantal eigenschappen. De eigenschappen zijn eenvoudig in te stellen. Het moeilijke is weten welke u cryptografische bescherming oplevert en welke slechts een beleefd verzoek, en de juiste toewijzingsvolgorde aanhouden zodat de versleuteling die u vroeg ook echt de versleuteling is die u krijgt

Wat de twee wachtwoorden werkelijk beloven

PDF-versleuteling definieert twee credentials met verschillende taken, en ze door elkaar halen is de meest voorkomende ontwerpfout in code voor beveiligde output. Het gebruikerswachtwoord regelt de ontsleuteling. Zonder dat, of het eigenaarswachtwoord, kan een conforme reader de bestandssleutel niet reconstrueren en blijft de inhoud cryptografisch onleesbaar. Het eigenaarswachtwoord regelt juist de toestellingsinstellingen: een reader die het eigenaarswachtwoord ontvangt, krijgt volledige toegang ongeacht wat de beperkingsvlaggen zeggen

De toestemmingsbits staan op zwakkere grond. Afdrukken, inhoud extraheren, formulieren invullen: elk is een vlag die een viewer leest en naar eigen inzicht respecteert (ISO 32000-2 §7.6.4). Versleuteling beschermt de bytes. De toestemmingsvlaggen sturen alleen conforme software, en pas nadat het feit plaatsvond. Iedereen die het document opent met het gebruikerswachtwoord houdt de ontsleutelde inhoud al in het geheugen, dus "niet kopiëren" en "niet afdrukken" betekenen iets voor een goed gedraagde viewer en niets voor een vastberaden gebruiker. Bouw het dreigingsmodel rond die grens. Vertrouwelijkheid zit in het gebruikerswachtwoord. Toestemmingen bepalen wat reguliere viewers aanbieden, en dat is alles wat ze doen

HotPDF-diagram van versleutelde PDF-gegevens waarbij het gebruikerswachtwoord de bestandssleutel afleidt en decryptie ontgrendelt, het eigenaarswachtwoord volledige toegang verleent die permissievlaggen overschrijft, en een waarschuwingsbalk vermeldt dat ProtectOptions-permissiebits verzoeken zijn die alleen door conforme software worden gehonoreerd
Het gebruikerswachtwoord draagt vertrouwelijkheid terwijl het eigenaarswachtwoord slechts beperkingen opheft; permissievlaggen sturen conforme viewers en binden niemand die kwaadwillend is

Configuratievolgorde: alles vóór BeginDoc

HotPDF bouwt de versleutelingsdictionary en leidt de bestandssleutel af op het moment dat BeginDoc draait. Wat de beschermingseigenschappen op dat moment bevatten is wat het document krijgt, en ze achteraf wijzigen verandert niets. De eigenschap die hier het meest telt is CryptKeyLength, die het schema kiest uit de THPDFKeyType-waarden k40, k128, aes128 en aes256. Wijs het toe na BeginDoc en u krijgt geen exceptie, geen waarschuwing, alleen een bestand dat stilletjes behield waarmee het begon. Dat soort stille afwijking is het ergste soort: het doorstaat elke lokale test en duikt maanden later op als een compliance-bevinding op het bureau van een klant

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'statement.pdf';
    Pdf.ActivateProtection := True;
    Pdf.CryptKeyLength := aes256;        // moet vóór BeginDoc gezet worden
    Pdf.UserPassword := 'open-secret';
    Pdf.OwnerPassword := 'admin-secret';
    Pdf.UseAES256R6 := False;            // R=5: breedste viewer-ondersteuning
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 720, 0, 'Account statement, June 2026');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Wachtwoorden zijn UTF-8 en beperkt tot 127 bytes, wat de ISO 32000-2-limiet is voor de AES-256-schema's. Als uw wachtwoordbeleid u langere secrets aanreikt, doet u de truncatie zelf, aan uw kant, waar u precies bepaalt waar de snede valt. Laat het aan het toeval over en de bibliotheek en een toekomstige viewer kunnen van mening verschillen over de afkapplaats, wat een bestand oplevert dat voor u opent en elders hetzelfde wachtwoord weigert

Revisie 5 of revisie 6: één boolean, twee ecosystemen

UseAES256R6 kiest tussen de twee AES-256-handshakes, en de keuze heeft meer gevolgen dan het booleaanse type suggereert. Laat het False en HotPDF schrijft revisie 5, het AES-256-schema dat als een uitbreiding op PDF 1.7 verscheen en dat ongeveer vijftien jaar aan viewers kunnen openen. Zet het True en u krijgt revisie 6, de versterkte key-derivation gestandaardiseerd in ISO 32000-2 voor PDF 2.0, die een bekende zwakte sluit in de manier waarop revisie 5 het wachtwoord verifieert

Revisie 6 is cryptografisch dus het betere verhaal. Het is ook degene die dingen breekt. Een revisie 6-bestand heeft een viewer nodig die gebouwd is voor PDF 1.7 Extension Level 3 of PDF 2.0, en veel uitgerolde software is geen van beide: records-management-archieven, ingebedde renderers in andere producten, line-of-business-tools die niemand in jaren heeft aangeraakt. Die weigeren het bestand ronduit, en ze doen het op de machine van de klant, nooit op de uwe. De praktische standaard is daarom revisie 5. Grijp pas naar revisie 6 wanneer een beveiligingsbeleid ISO 32000-2 bij revisie noemt, en wanneer u werkelijk hebt bevestigd dat elke consument het kan lezen. Hoe dan ook, schrijf op welke u koos en waarom, want de volgende persoon die deze code aanraakt zal het zich afvragen

De oudere sleuteltypen verdienen een zin zodat u weet ze over te slaan. THPDFKeyType somt nog steeds k40, k128 en aes128 op, maar ze bestaan om historische archieven te reproduceren, niet om nieuwe te beschermen. 40-bit RC4 bezwijkt voor commodity-hardware, en de 128-bit-schema's gaan vooraf aan de AES-256-revisies die een actuele beveiligingsreview verwacht. Voor een document dat u in 2026 aanmaakt is de echte vraag alleen revisie 5 versus revisie 6; als u zich voor een nieuw ontwerp tot de legacy-typen aangetrokken voelt, is er stroomopwaarts iets misgegaan

Toestemmingsvlaggen zonder open-wachtwoord

Vaak is de eis het tegenovergestelde van geheimhouding. Iedereen moet het document kunnen lezen, maar afdrukken of extractie moet beperkt blijven. U drukt dat uit met een leeg gebruikerswachtwoord en een niet-lege eigenaarswachtwoord, wat PDF de open-wachtwoordmodus noemt, en u somt de bewerkingen die u wilt toestaan op in ProtectOptions

Zij-aan-zij Delphi-stromen die tonen dat protectie-properties zoals CryptKeyLength aes256 vóór BeginDoc toegewezen een AES-256-bestand opleveren, terwijl dezelfde toewijzing na BeginDoc het oorspronkelijke schema onaangeroerd laat zonder exceptie of waarschuwing
BeginDoc bevriest het encryption-woordenboek, dus de juiste-volgorde-laan levert de gevraagde AES-256-revisie op terwijl de verkeerde-volgorde-laan stilletjes het standaardschema verscheept
Pdf.ActivateProtection := True;
Pdf.CryptKeyLength := aes256;
Pdf.UserPassword := '';                      // iedereen kan het bestand openen
Pdf.OwnerPassword := 'rotate-me-quarterly';  // beschermt de permissieset
Pdf.ProtectOptions := [prPrint, prPrint12bit, prExtractContent];
Pdf.BeginDoc;
// ... page content ...
Pdf.EndDoc;

De set THPDFProtectOptions mapt op de ISO-toestemmingsbits: prPrint en prPrint12bit voor hoogresolutie-afdrukken, prInformationCopy voor algemeen kopiëren en extractie, prExtractContent voor extractie door hulptechnologie, plus prModifyStructure, prEditAnnotations, prFillAnnotations en prAssemble. Twee ervan verdienen een waarschuwing. Laat prExtractContent aan in bijna elk profiel dat u bouwt. Het is de bit die een schermlezer nodig heeft om de tekst te bereiken, en het wissen zet stilletjes een rechtenbeslissing om in een toegankelijkheidsgebrek dat iemand met een beperking treft en u nooit ziet. De andere val is prPrint alleen, zonder prPrint12bit: verschillende viewers reageren door de afdrukkwaliteit te verlagen, en uw gebruikers dienen dat in als een renderingbug in plaats van de toestellingsinstelling die het werkelijk is

Verificatie kost vijf minuten en hoort in uw release-checklist. Open een voorbeeld van elk profiel in Acrobat, open Document Properties en lees het tabblad Security, dat het algoritme ("AES 256-bit") uitspelt en de toegestane bewerkingen één voor één opsomt. Open dan hetzelfde bestand in de oudste viewer die uw klanten werkelijk draaien, niet de nieuwste op uw machine. Die tweede open is de goedkope verzekering tegen een revisie 6-bestand dat probleemloos door ontwikkeling komt en sterft bij een klant die nooit upgradeerde

Bescherming verwijderen uit bestaande bestanden

Ontsleuteling draait hetzelfde eigenschappenmodel achterstevoren. Laad het document met een geldige credential, zet bescherming uit en sla het resultaat zonder bescherming op

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;   // encryptie laten vallen bij het opslaan
      Pdf.SaveLoadedDocument('plain.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Die route parseert het hele document in het geheugen, wat prima is voor gewone bestanden en verspillend voor grote. Wanneer de input in de honderden megabytes loopt, is DecryptFile de goedkopere optie: het ontsleutelt tijdens een kopie op bestandsniveau, en neemt een direct AES-256-herschrijfpad dat het bouwen van de volledige objectstructuur overslaat wanneer de input het toelaat. Het is deel van de Direct File API die behandeld wordt in het begeleidende artikel over het verwerken van grote PDF's vanuit Delphi

Beperkingen die met versleuteling samenhangen

Twee limieten zijn de moeite om te kennen voordat u rond versleuteling ontwerpt in plaats van erna. De eerste is archiefconformiteit. ISO 19005 verbiedt versleuteling in PDF/A, dus elke workflow die een document versleutelt én PDF/A-conformiteit claimt is constructief tegenstrijdig; HotPDF laat u niet beide in één bestand hebben. Wanneer u werkelijk beide nodig heeft, is het antwoord twee artefacten: een versleutelde kopie voor distributie en een aparte onversleutelde kopie voor het archief

De tweede limiet is harder. PDF-versleuteling kent geen escrow en geen herstel. Verlies het gebruikerswachtwoord op een R5- of R6-bestand en uw opties zijn brute force of opgeven. Behandel eigenaars- en gebruikerssecrets dus zoals u elke productie-credential behandelt. Genereer ze, bewaar ze in een kluis, roteer ze volgens een schema. Het enige wat u nooit moet doen is ze hardcoden als constanten in een unit, waar ze regelrecht in versiebeheer belanden en voor altijd in de werkende kopie van elke ontwikkelaar staan

Nog één reflex die het bouwen waard is. De bescherming wijzigen op een bestand dat u niet zelf maakte is dezelfde machinerie als ontsleuteling, geen aparte functie: laad het met zijn wachtwoord via LoadFromFile, bewerk ProtectOptions of de wachtwoorden op hun plek, en schrijf het terug met SaveLoadedDocument. Als u een bestand kunt ontsleutelen, kunt u het opnieuw van toestemmingen voorzien, en de code lijkt vrijwel identiek aan het voorbeeld hierboven

De beschermingseigenschappen die hier getoond worden zijn deel van de standaard HotPDF Delphi Component voor Delphi en C++Builder; de productpagina draagt de volledige versleutelingsreferentie, inclusief de complete toestellingsopsomming