PDF Library for Delphi kan öppna en krypterad PDF från dess råa filkrypteringsnyckel i stället för ett lösenord. DAOpenFileWithEncryptionKey tar emot nyckeln som hexadecimal text, kontrollerar den mot verifieraren som redan finns i krypteringsordboken och returnerar ett skrivskyddat Direct Access-handtag; DAOpenFromStreamWithEncryptionKey gör samma sak för en TStream som ägs av anroparen. Båda kom i v3.496.0
Scenariot är smalt och verkligt. Ett kriminaltekniskt uppdrag ger dig en nyckel som återvunnits ur en minnesavbildning men inget lösenord. En massarkivering har tiotusen dokument vars filnycklar ligger i en escrow-databas eftersom det ursprungliga DRM-systemet slutade utfärda lösenord för flera år sedan. En migrering bort från en avvecklad rättighetshanteringsprodukt har nyckelmaterial och inget annat. I alla dessa fall är uppgiften du har resultatet av nyckelhärledning, inte indata till den, och ingen lösenordsparameter i API:et kan ta emot den
Varför är en filkrypteringsnyckel inte ett lösenord?
Ett lösenord och en filkrypteringsnyckel befinner sig på varsin sida av nyckelhärledningen i PDF-standardens säkerhetshanterare (ISO 32000-1 §7.6.3). Hanteraren tar ett lösenord, blandar det med /O, /P, fil-ID:t och en versionsspecifik hash och producerar filnyckeln. Mata in en filnyckel i lösenordsfältet och du får nonsens som hashas till annat nonsens, vilket är varför detta behöver en egen ingångspunkt i stället för en flagga på DAOpenFile
Var den råa nyckeln kan injiceras bestäms av revisionen. Revisionerna 2 till 4 härleder fortfarande en separat objektpecifik nyckel från filnyckeln, objektnumret, generationsnumret och, för AESV2, AES-saltet, så att ha filnyckeln låter dig inte hoppa över härledningen på objektnivå alls. Revisionerna 5 till 7 använder den 32 byte långa filnyckeln direkt för AES-256, utan något objektsteg. Det enda lagret som är gemensamt för båda är själva filnyckeln, och det är den enda plats där PDF Library for Delphi accepterar en externt tillhandahållen nyckel. Om du fortfarande har lösenordet ska du i stället använda den vanliga vägen och låta livscykeln för lösenordsförsök hantera ett första felaktigt försök, eftersom rånyckelvägen medvetet avstår från flera bekvämligheter som lösenordsvägen behåller
Vilken indata accepterar DAOpenFileWithEncryptionKey?
Endast oförsett, blankstegsfritt och jämnlångt ASCII-hexadecimalt innehåll, och antalet avkodade byte måste exakt motsvara krypteringsrevisionen. För revisionerna 2 till 4 kommer den förväntade längden från /Length i krypteringsordboken: en multipel av 8 bitar som hamnar mellan 5 och 16 byte, med 40 bitar som standard när /Length saknas. För revisionerna 5 till 7 är den exakt 32 byte, utan förhandling. Ett 0x-prefix, ett udda antal siffror, fler än 64 hextecken, en okänd bit i Options eller ett dokument som inte är krypterat alls ger samma resultat: ett handtag på 0 och LastErrorCode satt till PDFLIB_ERROR_RAW_ENCRYPTION_KEY_INVALID, vilket är 425. Strängheten är poängen. En tolerant parser som tar bort blanksteg och fyller ut kort indata med nollor förvandlar gärna en avklippt inklistring från urklipp till en uppgift och misslyckas sedan någonstans som är mycket svårare att tolka
Var
Lib: TPDFlib;
FileHandle, PageRef: Integer;
Begin
Lib:= TPDFlib.Create;
Try
// KeyHex är 32 hextecken för AES-128 R4 och 64 för 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 övriga felkoderna förblir separata så att ett batchjobb kan skilja operatörsfel från problem med bevismaterialet: 411 när filen inte finns, 401 när den inte kan öppnas för läsning och 409 när korsreferensstrukturen är trasig. Allt som gäller nyckeln samlas avsiktligt i 425, eftersom en rånyckelingång som rapporterar vilken del av nyckeln som var fel skulle vara en oracle
Vad bevisar verifieringen egentligen?
PDF Library for Delphi bevisar att den angivna nyckeln hör till detta dokument genom den verifierare som krypteringsordboken redan bär på, och kontrollen skiljer sig mellan revisionerna. Revision 2 beräknar RC4-krypteringen av den 32 byte långa standardutfyllnaden på nytt och jämför alla 32 byte med /U. Revisionerna 3 och 4 hashar utfyllnaden tillsammans med fil-ID:t, kör RC4-passet plus de 19 XOR-härledda rundorna och jämför de första 16 byten av /U. Revisionerna 5 till 7 dekrypterar strängen /Perms på 16 byte med ett noll-IV och kontrollerar fyra oberoende saker samtidigt: behörighetsordet i little-endian mot /P, de fyra FF-byten på positionerna 5 till 8, flaggan för krypterade metadata som T eller F och markören adb på positionerna 10 till 12
När en verifierare finns men inte stämmer vägras öppningen ovillkorligen. Det är värt att säga tydligt, eftersom det är garantin hela funktionen vilar på. Lägg också märke till vad verifieringen inte är: den säger att nyckeln dekrypterar den här filen, inte att någon har gett dig tillstånd att använda den. Behörighetsordet som återvinns från /Perms är bevis om nyckeln, inte ett tillstånd, och om du vill veta vad dokumentet faktiskt påstår att det tillåter är det ett separat arbete för en granskningspass för kryptering och behörigheter. Problem med normalisering på lösenordssidan, som SASLprep-hantering av lösenord med icke-ASCII för AES-256, uppstår helt enkelt inte här eftersom ingen sträng någonsin når en hash
När gäller PDF_RAW_KEY_ALLOW_UNVERIFIED?
PDF_RAW_KEY_ALLOW_UNVERIFIED täcker exakt en situation: dokumentet har ingen användbar verifierare, eftersom /Perms saknas eller inte är 16 byte, eller eftersom /U är för kort för att jämföras. Den kan inte åsidosätta motstridiga bevis. Förstör en hexsiffra inne i /Perms och skicka den korrekta nyckeln med alternativet satt, så returnerar PDF Library for Delphi fortfarande 0 och 425. Skicka en nyckel med 32 nollbyte mot en intakt verifierare med alternativet satt, så blir svaret detsamma. Alternativet lättar på frånvaron av bevis, aldrig på en motsägelse av dem. Eftersom en återställningsöppning och en verifierad öppning är olika kunskapslägen rapporteras de också separat i stället för att vikas in i returvärdet, och DAGetEncryptionKeyValidation tar det öppnade handtaget och svarar med en av tre konstanter:
PDF_RAW_KEY_VALIDATION_VERIFIED(1) betyder att en verifierare fanns och stämdePDF_RAW_KEY_VALIDATION_UNVERIFIED(2) betyder att nyckeln accepterades bara eftersom ingen verifierare kunde utvärderas och anroparen uttryckligen begärde den policynPDF_RAW_KEY_VALIDATION_NONE(0) är vad ett vanligt lösenordsöppnat handtag rapporterar
FileHandle:= Lib.DAOpenFileWithEncryptionKey(CaseFile, KeyHex);
If (FileHandle= 0)And
(Lib.LastErrorCode= PDFLIB_ERROR_RAW_ENCRYPTION_KEY_INVALID) Then
// ingen verifierare i den här filen? försök igen med en uttrycklig återställningspolicy
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;
Skrivskyddad av konstruktionen, och vem äger strömmen?
Filingången för rånyckel öppnar alltid källan med fmOpenRead or fmShareDenyWrite och markerar hela Direct Access-kedjan som skrivskyddad, så DAAppendFile vägrar skrivning på plats och returnerar 2 i stället för att försöka med en inkrementell uppdatering. Det är ingen policy som du kan övertala handtaget att frångå; den sätts i konstruktorn innan filen ens parsas. För bevisarbete är den egenskap du vill ha att källbytena är byteidentiska efter att handtaget stängts, och regressionssviten hävdar exakt det för både ett AES-128-revisions-4-fixture och ett AES-256-revisions-6-fixture. Strömingången beter sig likadant vid skrivningar och lägger till en regel: DAOpenFromStreamWithEncryptionKey tar aldrig över ägarskapet, så DACloseFile lämnar din TStream vid liv och du frigör den själv
Source:= TMemoryStream.Create;
Try
Source.LoadFromFile(ArchivePath);
Source.Position:= 0;
FileHandle:= Lib.DAOpenFromStreamWithEncryptionKey(Source, KeyHex);
If FileHandle<> 0 Then
Try
// 2 = skrivskyddat handtag: exportera någon annanstans, lägg aldrig till i bevismaterialet
Assert(Lib.DAAppendFile(FileHandle)= 2);
Harvest(Lib, FileHandle);
Finally
Lib.DACloseFile(FileHandle);
End;
Finally
Source.Free; // handtaget ägde aldrig denna ström
End;
Nyckelhygien och vad DLL:en exporterar
När kedjan stängs skrivs filnyckeln, lösenordscachen och de härledda objektnycklarna över, och den avkodade nyckeln raderas i entry pointens Finally-block oavsett om öppningen lyckades eller inte. Det finns en subtilitet bakom detta som kostade verklig felsökningstid: varje kopia som måste överleva klonas uttryckligen med SetLength plus Move i stället för att tilldelas. Tilldela en AnsiString till en annan i Delphi så delar båda namnen en buffert genom copy-on-write, och om anroparens sida raderas nollställs nyckeln som krypthanteraren fortfarande använder, så dokumentet dekrypteras till skräp av orsaker som ingen stack trace skulle förklara. Bara de filbaserade ingångspunkterna passerar DLL-gränsen, i wide- och ANSI-former, tillsammans med accessorn för valideringsstatus; TStream-varianten förblir Delphi-only eftersom den beror på Delphis objektlivstid och referenssemantik som inte har någon ärlig representation i ett platt C-ABI. Om ditt återställningsverktyg är en DLL-klient bör du planera för att mellanlagra i en temporär fil och ta bort den under samma kontroller som du använder för nyckeln
Behandla hexnyckeln som autentiseringsmaterial med samma hanteringsregler som ett dokumentlösenord och spara valideringsstatusen i den logg som din kedja av bevisförvaring producerar, så att en senare läsare kan skilja en verifierad extrahering från en utan intyg. Om du utvärderar en Delphi PDF-komponent för kriminalteknik, massarkivering eller en DRM-migrering ingår rånyckelingångarna, skrivskyddsgarantin och ytan för krypteringsgranskning i samma bibliotek, och hela funktionslistan finns på produktsidan för PDF Library for Delphi