Een permissiebit is geen beveiligingsmechanisme. De bit die "niet kopiëren" zegt leeft binnen dezelfde /Encrypt-woordenlijst als de cryptografie, wat het een sfeer van handhaving geeft die het niet heeft, en het moment dat je de twee als één ding behandelt begint je audit verkeerde antwoorden te produceren. De enige vraag die de moeite waard is om aan een PDF te stellen is niet "is hij versleuteld." Het is specifieker en moeilijker: welk algoritme, welke security handler-revisie, welk van de twee wachtwoorden was ingesteld, welke permissiebits worden geclaimd, en welke delen van het bestand de versleuteling werkelijk raakt. Een bestand kan formeel versleuteld en praktisch open zijn. Het kan weigeren gelezen te worden en toch zijn metadata in platte tekst achterlaten. Het kan afdrukken vergrendelen in een bit die elke viewer vrij is te negeren. Een PDF auditen betekent al die afzonderlijk oplossen, en PDF Library for Delphi, losLab's PDF-motor voor Delphi en C++Builder, stelt elk van hen bloot door zowel een platte integer-handle-API als een getypte klasselaag
Wat de /Encrypt-woordenlijst werkelijk vastlegt
ISO 32000-1 §7.6 definieert documentbeveiliging via een handvol woordenlijstvermeldingen, en PDF Library for Delphi spiegelt ze één-op-één in het TPDFEncryption-record. De filterversie V en revisie R selecteren de algoritme-familie. Length draagt de sleutelgrootte. De permissiebits zitten in P, de validatiestrings voor eigenaar- en gebruikerswachtwoord in O en U (met OE en UE toegevoegd voor AES-256), een EncryptMetadata-vlag rijdt mee, en drie meer velden noemen de crypt-filters die respectievelijk op strings, streams en ingebedde bestanden worden toegepast
De waarde van dit record is dat het niets voor je interpreteert. Het overhandigt de ruwe woordenlijst en laat jou de conclusies trekken, wat precies is wat een audit nodig heeft. Het plaintext-binnen-versleuteld-geval verschijnt in StringFilterIdentity en StreamFilterIdentity: als een van beide true is, passeert de corresponderende data ongestoord door het Identity-filter, ongeacht wat de versleutelde status van het document rapporteert. Een scanner die stopt bij "een /Encrypt-woordenlijst is aanwezig" zal zo'n bestand beveiligd noemen terwijl zijn strings en streams in de klare zitten. Dezelfde nuance beheerst metadata. Als EncryptMetadata false is blijft het XMP-pakket leesbaar voor elke indexeerder terwijl de pagina-inhoud dat niet is, wat de moeite waard is om te weten zodra je routeringsregels op een titel- of auteursveld werken
Een korte beveiligingsprobe met de platte API
Voor de meeste pipelines beantwoorden vier platte aanroepen de alledaagse vragen. LoadFromFile retourneert 1 bij succes, en zodra het document open is rapporteren de versleutelingsinspectors tegen zijn ontsleutelde status:
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); // sleutellengteklasse
Writeln('owner pw? : ', PDF.CheckPassword(CandidatePassword));
finally
PDF.Free;
end;
end;
CheckPassword doet er meer toe dan zijn één-regelige handtekening suggereert. PDF definieert twee wachtwoorden met ongelijke macht. Het gebruikerswachtwoord is vereist om het bestand überhaupt te openen. Het eigenaarswachtwoord verleent volledige rechten en heft elke permissiebit op. De bytes op schijf zijn identiek, maar een sessie die onder het eigenaarswachtwoord is geopend kan dingen doen die de gebruikerswachtwoord-sessie niet kan, dus een audit die niet opneemt welke referentie werd gepresenteerd legt slechts de halve waarheid vast. De klasselaag maakt het onderscheid bevraagbaar. TPDFDocument.HasUserPassword en HasOwnerPassword rapporteren wat het bestand vereist, terwijl IsUserPassword en IsOwnerPassword rapporteren welk wachtwoord werkelijk de huidige sessie opende. Leg dat feit vast. Leg nooit de wachtwoordwaarden zelf vast
De Strength-ladder, waar "AES-256" twee dingen betekent
De platte Encrypt- en EncryptFile-functies nemen een geheel getal Strength met vijf betekenisvolle waarden: 0 voor 40-bit RC4, 1 voor 128-bit RC4, 2 voor 128-bit AES leesbaar vanaf Acrobat 7, 3 voor 256-bit AES zoals geïntroduceerd met Acrobat 9, en 4 voor 256-bit AES zoals vereist door Acrobat X en later
Het interessante deel is dat 3 en 4 beide AES-256 heten en niet hetzelfde schema zijn. Strength 3 mapt op security handler-revisie 5, een interim-ontwerp dat Acrobat 9 verzond en ISO nooit aannam. Strength 4 mapt op revisie 6, wiens key-derivation-functie werd verhard en gestandaardiseerd in ISO 32000-2. Voor een document dat je vandaag maakt is er geen reden om 3 boven 4 te kiezen. Voor een audit is de kloof beslissend: een beleid dat "AES-256 volgens ISO 32000-2" leest wordt alleen door R6 vervuld, en een R5-bestand dat zichzelf AES-256 noemt faalt dat beleid terwijl het een naïeve strength-check doorstaat. De klasselaag houdt de twee uit elkaar op naam, esAES256Bit voor R5 tegenover esAES256BitAcroX voor R6, en de eigenschap EncryptionAcroX beantwoordt de revisievraag met één boolean
Permissiebits en hun kleine-letter fine print
EncodePermissions pakt acht vlaggen in het gehele getal dat Encrypt en EncryptFile verwachten. Printen, kopiëren, wijzigen en notities-toevoegen vormen de basisset; velden-vullen, kopiëren-voor-toegankelijkheid, assembleren en volledige-kwaliteit-printen vormen de uitgebreide set. De fine print, die de eigen encryptie-demo van de bibliotheek ronduit stelt, is dat de uitgebreide vier alleen effect hebben bij 128-bit-strength en hoger. De volledige-kwaliteit-print-vlag valt onder dezelfde regel: wis hem om afdrukken met lage resolutie af te dwingen en een 40-bit document negeert je, want die downgrading vereist ook 128-bit of sterkere versleuteling. Codeer een "alleen-lage-resolutie-print"-beleid in een 40-bit bestand en elke viewer drukt toch op volledige kwaliteit
De diepere vraag is wie deze bits überhaupt handhaaft, en het antwoord is niemand die je kunt vertrouwen. Permissies zijn instructies aan conforming readers, geen cryptografische restricties. De ontsleutelingssleutel is identiek of kopiëren nu is toegestaan of geweigerd, dus een vergrendelde permissieset houdt alleen eerlijke viewers eerlijk. Een reader die kiest de bits te negeren staat voor geen cryptografische obstakel. Als de verplichting extractie te voorkomen in plaats van het tegen te houden is, heeft het bestand een gebruikerswachtwoord nodig en heeft de workflow procesniveau-controles eromheen nodig, en een auditr rapport moet benoemen onder welk van de twee regimes elk bestand werkelijk valt in plaats van een permissiebit als een slot te behandelen
Beleid instellen en bewijzen dat het bleef plakken
Versleuteling op bestaande bestanden toepassen vereist niet ze in de objectboom te laden. EncryptFile verwerkt input naar output in één aanroep, en de auditlus heropent het resultaat om te bevestigen wat op schijf belandde. De meegeleverde encryptie-demo volgt dezelfde schrijf-dan-lees-terug-vorm:
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: alleen printen op volle kwaliteit
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;
Teams die op de documentlaag werken krijgen dezelfde bewerking met getypte sets in plaats van bit-packing, wat code-review overleeft met veel minder geknijp:
if not Doc.Encrypt('owner-secret', 'user-secret', esAES256BitAcroX,
[ppCanPrint], [ppCanPrintFull]) then
raise Exception.Create('Encryption failed');
hoe dan ook, de lees-terug-stap is geen optionele ceremonie. Het vangt de implementatiefouten die anders maanden later op de machine van een klant opduiken: een oude bibliotheek-build die de gevraagde strength stil downgradet, een output-pad dat nooit werd geschreven omdat de directory alleen-lezen was, een permissies-geheel-getal waarvan de argumenten in de verkeerde volgorde gingen. Alle drie doorstaan ze een lokale rooktest en falen in het veld, en de output heropenen verandert elk van hen in een uitzondering die je ziet tijdens de run die het bestand maakte. GetEncryptionFingerprint retourneert een compacte waarde die je bij de jobrecord kunt opslaan, zodat een latere vergelijking kan vertellen of twee outputs dezelfde versleutelingsconfiguratie delen zonder een van beide te heropenen
Audit-fout-positieven die de moeite van coderen waard zijn
Een paar patronen duwen beveiligingsscanners betrouwbaar naar de verkeerde conclusie, en elk komt voort uit een meerdelige vraag tot een ja-of-nee-antwoord reduceren. Het Identity crypt-filter is het zuiverste voorbeeld. Een /Encrypt-woordenlijst is aanwezig, het bestand rapporteert als versleuteld, en toch lopen de strings en streams ongewijzigd door het Identity-filter, dus de werkelijke inhoud is platte tekst. StringFilterIdentity en StreamFilterIdentity lezen voordat je iets als beschermd declareert is de oplossing
De metadata-splitsing is subtieler. EncryptMetadata kan het oneens zijn met de rest van het document in beide richtingen, waardoor een versleuteld bestand met een leesbaar XMP-pakket blijft, of minder vaak, het omgekeerde. "Het bestand is versleuteld" zegt niets over of zijn metadata dat ook is, wat uitmaakt zodra een indexeerder of routeringsregel naar de titel grijpt. Ingebedde bestanden voegen een derde as toe: PDF staat een specifiek crypt-filter alleen voor bijlagen toe, dus de bijlagen kunnen het enige versleutelde deel van een anders open document zijn, of het enige platte-tekstdeel van een versleuteld document. Leg de drie filtertoewijzingen vast als afzonderlijke velden voor strings, streams en ingebedde bestanden, en geen van deze valstrikken kan je verrassen. Sla één boolean op en de verkeerde aanroep is slechts een kwestie van tijd
Versleuteling verwijderen, en het kiezen voor nieuwe bestanden
Een audit eindigt vaak in een beslissing om bescherming te strippen, en de mechaniek is daar niet het obstakel. DecryptFile(InputFileName, OutputFileName, Password) schrijft een ontsleutelde kopie zonder volledige lading, en het geladen-document Decrypt doet hetzelfde in het geheugen als een bestand al open is. Beide vereisen een geldig wachtwoord; geen van beide omzeilt de cryptografie. De echte poort is beleid in plaats van code, dus maak je intake-regels duidelijk over wanneer verwijdering is toegestaan en neem de wachtwoordklasse op die het autoriseerde, want de technische stap zelf laat geen spoor achter
De keuze voor nieuwe output is smaller dan de vijf Strength-waarden doen vermoeden. Gebruik Strength 4, AES-256 revisie 6, tenzij je bestanden moet openen in viewers ouder dan Acrobat X. Strength 2, AES-128, is de pragmatische vloer voor een verouderende viewer-vloot die niet kan worden geüpgraded. De RC4-opties op 0 en 1 zijn er zodat je historische archieven kunt lezen en auditen, niet zodat je er iets nieuws mee kunt produceren; ze grijpen in een 2026-ontwerp betekent dat een vereiste stroomopwaarts verouderd is
Versleutelingsstatus voedt direct door in handtekeningbeslissingen, aangezien een workbench die documenten valideert en ondertekent dezelfde lees-terug-discipline nodig heeft die deze audit ernaar verwijst. Die grond wordt behandeld in het artikel over de compliance- en handtekenwerkbench. Als een batch EncryptFile over duizenden grote documenten toepast, toont de directe-toegangsgids voor grote PDF's hoe je het geheugen vlak te houden terwijl het draait. De volledige encryptie-API-referentie staat op de PDF Library for Delphi-productpagina