PDF Library for Delphi kan åpne en kryptert PDF fra den rå filkrypteringsnøkkelen i stedet for et passord. DAOpenFileWithEncryptionKey tar nøkkelen som heksadesimal tekst, kontrollerer den mot verifikatoren som allerede ligger i krypteringsordboken og returnerer et skrivebeskyttet Direct Access-håndtak; DAOpenFromStreamWithEncryptionKey gjør det samme for en TStream som caller-en eier. Begge kom i v3.496.0
Situasjonen er smal, men reell. Et etterforskningsoppdrag gir deg en nøkkel hentet fra et minnebilde, men ikke noe passord. En massearkivering har ti tusen dokumenter der filnøklene ligger i en escrow-database fordi DRM-systemet som opprettet dem, sluttet å utstede passord for mange år siden. En migrering bort fra et utfaset rettighetsstyringsprodukt har nøkkelmaterialet, men ingenting annet. I alle disse tilfellene er legitimasjonen du har, resultatet av nøkkelavledning, ikke inndataen, og ingen passordparameter i API-et kan ta imot den
Hvorfor er en filkrypteringsnøkkel ikke et passord?
Et passord og en filkrypteringsnøkkel ligger på hver sin side av nøkkelavledningen i PDF-standardens sikkerhetshåndterer (ISO 32000-1 §7.6.3). Håndtereren blander et passord med /O, /P, fil-ID-en og en revisjonsspesifikk hash, og produserer filnøkkelen. Mater du en filnøkkel inn i passordplassen, blir resultatet meningsløst hashet til et annet meningsløst resultat. Derfor trenger funksjonen sitt eget entry point i stedet for et flagg på DAOpenFile
Hvor rånøkkelen kan settes inn, bestemmes av revisjonen. Revisjon 2 til 4 avleder fortsatt en egen nøkkel per objekt fra filnøkkelen, objektnummeret, generasjonsnummeret og, for AESV2, AES-saltet, så det å ha filnøkkelen lar deg ikke hoppe over avledningen på objektnivå. Revisjon 5 til 7 bruker 32-byte filnøkkelen direkte til AES-256, uten et trinn per objekt. Det ene laget som er felles for begge, er selve filnøkkelen, og det er derfor det eneste stedet PDF Library for Delphi godtar en nøkkel levert utenfra. Hvis du fortsatt har passordet, bør du bruke den vanlige stien og la passordets retry-livssyklus håndtere et feil første forsøk, fordi rånøkkel-entry point-et med vilje gir avkall på flere bekvemmeligheter som passordstien beholder
Hvilken input godtar DAOpenFileWithEncryptionKey?
Bare u prefikset, mellomromsfri ASCII-heks, med partall lengde, og det dekodede antallet byte må stemme nøyaktig med krypteringsrevisjonen. For revisjon 2 til 4 kommer forventet lengde fra /Length i krypteringsordboken: et multiplum av 8 bit som ligger mellom 5 og 16 byte, med 40 bit som standard når /Length mangler. For revisjon 5 til 7 er lengden nøyaktig 32 byte, uten forhandling. Et 0x-prefiks, et oddetall siffer, mer enn 64 heksadesimale tegn, en ukjent bit i Options eller et dokument som ikke er kryptert, gir alle samme resultat: et håndtak på 0 og LastErrorCode satt til PDFLIB_ERROR_RAW_ENCRYPTION_KEY_INVALID, som er 425. Strengheten er poenget. En ettergivende parser som trimmer mellomrom og fyller korte inndata med nuller, gjør lett en avkuttet utklippstavleliming om til en legitimasjon og feiler deretter et langt mindre lesbart sted
Var
Lib: TPDFlib;
FileHandle, PageRef: Integer;
Begin
Lib:= TPDFlib.Create;
Try
// KeyHex er 32 heksadesimale tegn for AES-128 R4, 64 for AES-256 R6
FileHandle:= Lib.DAOpenFileWithEncryptionKey(CaseFile, KeyHex);
If FileHandle= 0 Then
Raise Exception.CreateFmt('raw key refused (error %d)', [Lib.LastErrorCode]);
Try
PageRef:= Lib.DAFindPage(FileHandle, 1);
WriteLn(Lib.DAExtractPageText(FileHandle, PageRef, 0));
Finally
Lib.DACloseFile(FileHandle);
End;
Finally
Lib.Free;
End;
End;
De andre feilkodene forblir forskjellige, slik at en batchjobb kan skille operatørfeil fra problemer med bevismaterialet: 411 når filen ikke finnes, 401 når den ikke kan åpnes for lesing og 409 når kryssreferansestrukturen er ødelagt. Alt som gjelder nøkkelen, samles med hensikt i 425, fordi et rånøkkel-entry point som forteller hvilken del av nøkkelen som var feil, er en oracle
Hva beviser verifikasjonen egentlig?
PDF Library for Delphi beviser at den oppgitte nøkkelen hører til dette dokumentet, ved å bruke verifikatoren som krypteringsordboken allerede har, og kontrollen varierer etter revisjon. Revisjon 2 beregner RC4-krypteringen av den 32 byte lange standardutfyllingsstrengen på nytt og sammenligner alle 32 byte med /U. Revisjon 3 og 4 hasher utfyllingen sammen med fil-ID-en, kjører RC4-runden pluss de 19 XOR-avledede rundene og sammenligner de første 16 byte av /U. Revisjon 5 til 7 dekrypterer /Perms-strengen på 16 byte med en null-IV og kontrollerer fire uavhengige ting samtidig: little-endian-tillatelsesordet mot /P, de fire FF-byte-ene på posisjon 5 til 8, flagget for kryptering av metadata som T eller F og adb-markøren på posisjon 10 til 12
Når en verifikator finnes, men ikke stemmer, avvises åpningen uten unntak. Det er verdt å si tydelig, fordi dette er garantien hele funksjonen bygger på. Legg også merke til hva verifikasjonen ikke er: Den sier at nøkkelen dekrypterer denne filen, ikke at noen har autorisert deg til å bruke den. Tillatelsesordet som ble hentet fra /Perms, er bevis på nøkkelen, ikke en tillatelse, og hvis du vil vite hva dokumentet faktisk hevder å tillate, er det en separat jobb for en krypterings- og tillatelseskontroll. Passordsidens normaliseringsproblemer, som SASLprep-håndtering av ikke-ASCII AES-256-passord, oppstår ganske enkelt ikke her, siden ingen streng noen gang når en hash
Når gjelder PDF_RAW_KEY_ALLOW_UNVERIFIED?
PDF_RAW_KEY_ALLOW_UNVERIFIED dekker nøyaktig én situasjon: Dokumentet har ingen brukbar verifikator, fordi /Perms mangler eller ikke er 16 byte, eller fordi /U er for kort til å sammenlignes. Det kan ikke overstyre bevis som feiler. Korrumper ett heksadesimalt tegn inne i /Perms og send den riktige nøkkelen med alternativet satt, så returnerer PDF Library for Delphi fortsatt 0 og 425. Send en nøkkel med 32 nullbyte mot en intakt verifikator med alternativet satt, så er svaret det samme. Alternativet lemper på fravær av bevis, aldri på en motsigelse av det. Fordi en gjenopprettingsåpning og en verifisert åpning er to forskjellige kunnskapstilstander, rapporteres de også separat i stedet for å brettes inn i returverdien, og DAGetEncryptionKeyValidation tar åpningens håndtak og svarer med én av tre konstanter:
PDF_RAW_KEY_VALIDATION_VERIFIED(1) betyr at en verifikator var til stede og stemtePDF_RAW_KEY_VALIDATION_UNVERIFIED(2) betyr at nøkkelen bare ble godtatt fordi ingen verifikator kunne evalueres, og caller-en ba uttrykkelig om denne policyenPDF_RAW_KEY_VALIDATION_NONE(0) er det et håndtak som ble åpnet med et vanlig passord, rapporterer
FileHandle:= Lib.DAOpenFileWithEncryptionKey(CaseFile, KeyHex);
If (FileHandle= 0)And
(Lib.LastErrorCode= PDFLIB_ERROR_RAW_ENCRYPTION_KEY_INVALID) Then
// ingen verifikator i denne filen? prøv på nytt med en eksplisitt gjenopprettingspolicy
FileHandle:= Lib.DAOpenFileWithEncryptionKey(CaseFile, KeyHex,
PDF_RAW_KEY_ALLOW_UNVERIFIED);
If FileHandle<> 0 Then
Begin
Case Lib.DAGetEncryptionKeyValidation(FileHandle) Of
PDF_RAW_KEY_VALIDATION_VERIFIED:
Chain.Note('key verified against the encryption dictionary');
PDF_RAW_KEY_VALIDATION_UNVERIFIED:
Chain.Note('no verifier available: extraction is unattested');
End;
End;
Skrivebeskyttet av konstruksjon, og hvem eier strømmen?
Fil-entry point-et for rånøkkelen åpner alltid kilden med fmOpenRead or fmShareDenyWrite og merker hele Direct Access-kjeden som skrivebeskyttet, så DAAppendFile nekter å skrive på stedet og returnerer 2 i stedet for å forsøke en inkrementell oppdatering. Dette er ikke en policy du kan overtale håndtaket til å ignorere; den settes i konstruktøren før filen i det hele tatt parses. For bevisarbeid er egenskapen du vil ha, at kildebyte-ene er byte-identiske etter at håndtaket lukkes, og regresjonstesten hevder nøyaktig dette for både et AES-128-revisjon-4-fixture og et AES-256-revisjon-6-fixture. Strøm-entry point-et oppfører seg likt ved skriving og legger til én regel: DAOpenFromStreamWithEncryptionKey tar aldri eierskap, så DACloseFile lar din TStream leve, og du frigjør den selv
Source:= TMemoryStream.Create;
Try
Source.LoadFromFile(ArchivePath);
Source.Position:= 0;
FileHandle:= Lib.DAOpenFromStreamWithEncryptionKey(Source, KeyHex);
If FileHandle<> 0 Then
Try
// 2 = skrivebeskyttet håndtak: eksporter et annet sted, legg aldri til i bevismaterialet
Assert(Lib.DAAppendFile(FileHandle)= 2);
Harvest(Lib, FileHandle);
Finally
Lib.DACloseFile(FileHandle);
End;
Finally
Source.Free; // håndtaket har aldri eid denne strømmen
End;
Nøkkelhygiene og hva DLL-en eksporterer
Å lukke kjeden overskriver filnøkkelen, passordcachen og de avledede objektnøklene, og den dekodede nøkkelen nullstilles i Finally-blokken til selve entry point-et, uansett om åpningen lyktes eller ikke. Det finnes en detalj bak dette som kostet reell feilsøkingstid: En kopi som må overleve, klones eksplisitt med SetLength pluss Move i stedet for å tildeles. Tildeler du én AnsiString til en annen i Delphi, deler begge navn én buffer under copy-on-write, så det å nullstille caller-siden ville nullstilt nøkkelen kryptohåndtereren fortsatt bruker, og dokumentet ville blitt dekryptert til søppel av grunner ingen stack trace kunne forklare. Bare de filbaserte entry points krysser DLL-grensen, i wide- og ANSI-former, sammen med aksessoren for valideringsstatus. TStream-varianten er Delphi-only, fordi den avhenger av levetid og referansesemantikk for Delphi-objekter som ikke har en ærlig representasjon i en flat C-ABI. Hvis gjenopprettingsverktøyet ditt er en DLL-klient, bør du planlegge å stage til en midlertidig fil og slette den under de samme kontrollene som du bruker for nøkkelen
Behandle heksnøkkelen som legitimasjonsmateriale med de samme håndteringsreglene som du ville gitt et dokumentpassord, og før valideringsstatusen i den loggen som kjeden av forvaring produserer, slik at en senere leser kan skille en verifisert uthenting fra en uten attestasjon. Hvis du vurderer en Delphi PDF-komponent for etterforskning, massearkivering eller en DRM-migrering, er rånøkkel-entry points, den skrivebeskyttede garantien og flaten for krypteringskontroll deler av det samme biblioteket, og hele funksjonslisten finner du på produktsiden for PDF Library for Delphi