HotPDF šifrira PDF za določene imetnike certifikatov skozi upravljalnik varnosti z javnimi ključi ISO 32000: EnablePubKeyEncryption vzame 20-bajtno naključno seme, vsak prejemnik pa dobi svojo ovojnico CMS, zgrajeno s AddPubKeyRecipientCertificate za ključe RSA (prenos ključev RSA-OAEP) ali AddPubKeyAgreementRecipientWithSecret za ključe eliptičnih krivulj (ECDH na P-256, P-384, P-521, X25519 ali X448). Nihče ne deli gesla; kdor drži ujemajoč se zasebni ključ, odpre datoteko
Primer uporabe je vedno nekaka različica iste zgodbe. Četrtletni paket revizije gre trem zunanjim recenzentom, pravna služba želi, da ga vsak prebere, samo enemu je dovoljeno tiskati, in nikomur ni do gesla, ki bi sedelo v niti e-pošte poleg priloge. Šifriranje z geslom tega ne zna izraziti. Šifriranje s certifikatom zna, ker vsak prejemnik odklene dokument s ključem, ki ga že ima, vsak prejemnik pa lahko v svoji ovojnici nosi drugačen nabor dovoljenj
Kako se šifriranje PDF s certifikatom razlikuje od gesla?
Šifriran PDF z javnimi ključi izpelje svoj ključ datoteke iz naključnega semena plus točnih bajtov vsake prejemniške ovojnice in ne iz česar koli, kar bi vtipkal človek. Upravljalnik je opisan v ISO 32000-1 §7.6.4 (§7.6.5 v ISO 32000-2), ovojnice pa so strukture CMS EnvelopedData, kot jih definira RFC 5652. HotPDF zapiše /Filter /Adobe.PubSec s /SubFilter /adbe.pkcs7.s5; za AES-256 to pomeni /V 5 in vnos /DefaultCryptFilter pod /CF z /CFM /AESV3, polje /Recipients pa živi znotraj tega kripto filtra. Vsaka ovojnica šifrira 24 bajtov: 20-bajtno seme, ki mu sledi 32-bitna beseda dovoljenj tega prejemnika. Vrednost /P v šifrirnem slovarju je le ogradek, ker prava dovoljenja potujejo znotraj vsake ovojnice. Ob nalaganju bralnik odvije eno ovojnico, povrne seme in seme hasha skupaj z vsako ovojnico v vrstnem redu /Recipients (SHA-256 za AES-256, SHA-1 za starejše šifre), da znova zgradi ključ datoteke. Če se še odločate med tem modelom in navadnimi gesli, vodnik o šifriranju z geslom AES-256 in zastavicami dovoljenj pokriva drugo stran tega kompromisa
Zapisovanje prejemnikov RSA z EnablePubKeyEncryption
Za certifikate RSA pokličite EnablePubKeyEncryption z aes256, nato pa pokličite AddPubKeyRecipientCertificate enkrat na certifikat, kodiran v DER, pred BeginDoc. Pomožnik zgradi ovojnico RSAES-OAEP v procesu z vrednostmi THPDFRSAOAEPHash za povzetek OAEP in povzetek MGF1 (rohSHA256, rohSHA384 ali rohSHA512), vsebino ovojnice pa šifrira z AES-256-CBC
uses
System.SysUtils, System.IOUtils, HPDFDoc, HPDFCrypt, HPDFRSA;
procedure WriteAuditPack(const OutFile: string);
var
Pdf: THotPDF;
Seed: AnsiString;
begin
SetLength(Seed, 20); // točno 20 bajtov, tudi za AES-256
AESGenerateRandomBytes(@Seed[1], Length(Seed));
Pdf := THotPDF.Create(nil);
try
Pdf.AutoLaunch := False;
Pdf.FileName := OutFile;
Pdf.EnablePubKeyEncryption(Seed, aes256, True); // privzeti tip ključa je aes128
// Recenzent A sme tiskati; recenzent B sme le brati in izvleči
Pdf.AddPubKeyRecipientCertificate(TFile.ReadAllBytes('reviewer-a.cer'),
[prPrint, prPrint12bit, prExtractContent], rohSHA256, rohSHA256);
Pdf.AddPubKeyRecipientCertificate(TFile.ReadAllBytes('reviewer-b.cer'),
[prExtractContent]);
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(72, 720, 0, 'Q3 audit pack');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Tri podrobnosti v tem seznamu nosijo obremenitev. Prvič, dolžina semena je fiksirana na 20 bajtov za vsak tip ključa, vključno z AES-256; EnablePubKeyEncryption sproži izjemo ob kateri koli drugi dolžini. Drugič, EnablePubKeyEncryption privzame aes128, oba pomožnika za certifikate pa odklonita delo, razen če je tip ključa aes256, zato pozabljen drugi argument prinese izjemo »certificate envelopes require aes256«. Zapuščinske šifre (k40, k128, aes128) še vedno delujejo, a le skozi AddPubKeyRecipient z ovojnico, ki ste jo zgradili drugje. Tretjič, šifriranje z javnimi ključi AES-256 je zmožnost PDF 2.0, zato HotPDF samodejno dvigne različico dokumenta na 2.0. Z nastavljenim StrictVersionLock na nižji različici se EnablePubKeyEncryption vrne, brez da bi kaj omogočil, odpoved pa se pokaže šele v naslednji vrstici kot »call EnablePubKeyEncryption first«. Preklop šifriranja med priraščajočo posodobitvijo takoj sproži EInvalidOpException
Dodajanje prejemnikov ECDH: P-256, P-384, P-521, X25519 in X448
Za certifikate eliptičnih krivulj AddPubKeyAgreementRecipientWithSecret zapiše prejemnika sporazuma o ključih CMS (KeyAgreeRecipientInfo, struktura KARI iz RFC 5753, s profilom X25519 in X448 iz RFC 8418) in izračuna skupno skrivnost ECDH v procesu. Krivuljo izberete z vrednostjo THPDFPubKeyAgreementScheme: pkasECDHP256, pkasECDHP384, pkasECDHP521, pkasX25519 ali pkasX448. Shema se mora ujemati s ključem v certifikatu, sicer klic sproži »Certificate key does not match the requested agreement scheme«. Pod kapoto vsaka ovojnica dobi svež naključni 32-bajtni UKM, ključ za šifriranje ključev, izpeljan s stdDH KDF (SHA-256 za P-256 in X25519, SHA-384 za P-384, SHA-512 za P-521 in X448), in ovijanje ključev AES-256, kot ga definira RFC 3394. Sama skupna skrivnost pride iz čisto pascalovske kode krivulj, brez vpletenega platformnega kripto ponudnika; članek o čisto pascalovski aritmetiki krivulj NIST razloži, kako je bila ta plast zgrajena in preverjena. Za Montgomeryjeve krivulje se celoten kratkotrajni par ključev lahko ustvari krajevno:
uses
System.SysUtils, System.IOUtils, HPDFDoc, HPDFCrypt, HPDFPubSec,
HPDFKeyAgreement;
procedure AddLegalRecipient(Pdf: THotPDF);
var
Scalar, OriginatorPublic: TBytes;
begin
// Svež kratkotrajni skalar na ovojnico; prijem se zgodi znotraj ladder
SetLength(Scalar, 32);
AESGenerateRandomBytes(@Scalar[0], Length(Scalar));
try
OriginatorPublic := HPDFX25519PublicFromScalar(Scalar);
Pdf.AddPubKeyAgreementRecipientWithSecret(
TFile.ReadAllBytes('legal-x25519.cer'),
[prPrint, prExtractContent], pkasX25519,
OriginatorPublic, Scalar,
[]); // OwnPublicPoint: smiselno le za krivulje NIST
finally
HPDFSecureClearBytes(Scalar);
end;
end;
Krivulje NIST zahtevajo od klicatelja več. HotPDF dobavlja pomožnike z javnimi ključi le za X25519 in X448 (HPDFX25519PublicFromScalar, HPDFX448PublicFromScalar), zato za P-256, P-384 in P-521 kratkotrajni par ključev ustvarite s svojim orodjem in podate skalar big-endian točno velikosti polja (32, 48 ali 66 bajtov) plus pripadajočo nestisnjeno točko 0x04||X||Y kot OriginatorPublicKey. HotPDF preveri prejemnikovo točko proti enačbi krivulje, ne more pa preveriti, da vaš izvorni javni ključ dejansko pripada vašemu skalarju. Neujemajoči se polovici še vedno izdelata povsem lepo oblikovano ovojnico, ki je noben prejemnik ne more odpreti, zato povratno nalaganje pripada vaši testni zbirki in ne samo preizkusu velikosti datoteke
Zakaj je vrstni red /Recipients pomemben?
Vrstni red /Recipients je pomemben, ker je ključ datoteke povzetek nad semenom in vsako ovojnico v vrstnem redu polja, zato morata zapisovalec in bralnik hashirati iste bajte v istem zaporedju. HotPDF ovojnice hrani v vrstnem redu, v katerem jih dodate, in jih zapiše nespremenjene, kar pomeni, da lahko prejemnike dodate v kakršnem koli vrstnem redu, nič nadalje pa ne sme prerazporediti, znova kodirati ali »počistiti« tega polja. Večina pravih napak na tem področju je bila variacija na to temo, kjer sta dve strani hashirali malenkostno različne bajte:
- Shranjevanje dinamičnih polj v
TListprekAddobdrži le surovi kazalec, medtem ko števec referenc ostane pri krajevni spremenljivki. NaslednjiSetLengthsprosti medpomnilnik in ga lahko ponovno uporabi, zato je vsako mesto nazadnje zasedalo vzdevek zadnje ovojnice in večprejemniške datoteke so izpeljale napačen ključ. Popravek je shraniti lastnjeno kopijo sList.Add(Pointer(System.Copy(Bytes))) - Odvijanje ovojnic razčleni DER na mestu, prehod za povrnitev ključa pa je prvotno hashiral ista živa polja. Bralnik si zdaj vzame posnetke nedotaknjenih kopij vsake ovojnice, preden katero koli odvijanje nanje poseže, povzetek pa teče čez posnetke
- Binarni DER, prehojen skozi Unicode
TStringList, dobi bajte od$80navzgor znova kodirane po kodni strani, zato HotPDF ovojnice hrani interno kot šestnajstiško besedilo - Šifrirani in binarni nizi se morajo zapisati kot šestnajstiški nizi. Dobesedni niz je podvržen normalizaciji koncev vrstic, kjer CR, LF in CRLF vsi postanejo en sam LF (ISO 32000-1 §7.3.4.2), in to tiho prepiše šifrirno besedilo. HotPDF odda vsak vnos
/Recipientskot šestnajstiški niz in ga izvzame iz šifriranja nizov, ker vsak bralnik potrebuje ovojnice, preden drži kakršen koli ključ - Prvi bajt DER
BIT STRINGšteje neuporabljene bite in mora biti nič za bajtno poravnane ključe. Pustiti ga neinicializiranega poSetLengthje zapisalo, kar je bilo na skladu, strog odvijalnik pa je zavrnil izvorni ključ, zato je datoteka lahko občasno odpovedala odprtje s prav tistim ključem, za katerega je bila zapisana - Ko isti ključ še vedno ne more dešifrirati, primerjajte plast za plastjo: ključ datoteke, nato predpono šifrirnega besedila (IV), nato ključ objekta, nato odprto besedilo. Napaka živi takoj za prvo plastjo, ki se ne strinja
Kako odprete šifriran PDF s certifikatom z zasebnim ključem?
Za odprtje šifriranega PDF s certifikatom registrirajte material zasebnega ključa, preden pokličete LoadFromFile, ker HotPDF povrne ključ datoteke med strukturnim prehodom. Ključ RSA ali EC, razčlenjen s HPDFParsePFX, dodelite PubSecKeyMaterial, dodatne ključe RSA dodajte s AddPubSecKeyMaterial, surove skalre ECDH pa registrirajte s AddPubSecAgreementKeyMaterial(CurveOID, PrivateScalar, OwnPublicPoint) s konstantami HPDFOIDX25519, HPDFOIDX448, HPDFOIDECP256, HPDFOIDECP384 ali HPDFOIDECP521. Krivulje NIST zahtevajo prejemnikovo lastno nestisnjeno javno točko; Montgomeryjeve krivulje jo prezrejo
uses
System.SysUtils, System.IOUtils, HPDFDoc, HPDFPFX, HPDFKeyAgreement;
procedure OpenAuditPack(const LegalScalar: TBytes);
var
Reader: THotPDF;
begin
Reader := THotPDF.Create(nil);
try
Reader.AutoLaunch := False;
Reader.PubSecKeyMaterial :=
HPDFParsePFX(TFile.ReadAllBytes('reviewer-a.pfx'), 'pfx-password');
Reader.AddPubSecAgreementKeyMaterial(HPDFOIDX25519, LegalScalar, nil);
// Neobvezno: izberite ovojnico neposredno, namesto da bi poskusili vse
Reader.PubSecRecipientQuery :=
function(Context: Pointer; RecipientCount: Integer): Integer
begin
Result := -1; // -1 = poskusi vsako ovojnico po vrsti
end;
Reader.LoadFromFile('audit-pack.pdf', '');
Writeln('Pages: ', Reader.GetLoadedPageCount);
finally
Reader.Free;
end;
end;
Brez povratnega klica HotPDF poskusi vsako ovojnico proti vsakemu registriranemu ključu: najprej primarni ključ, nato vsak dodatni ključ RSA, nato material EC. PubSecRecipientQuery prejme število ovojnic in vrne kazalo od 0 ali -1, kazalo zunaj polja pa sproži izjemo, namesto da bi bilo pritegnjeno. Upoštevajte, da AddPubSecKeyMaterial sprejme le material RSA (insistira na modulu in zasebnem eksponentu), zato ključi EC pripadajo v PubSecKeyMaterial ali AddPubSecAgreementKeyMaterial. Ko noben ključ ne odvije nobene ovojnice, korak za povrnitev vrne brez ključa datoteke, namesto da bi sprožil izjemo, zato preverite, da se je vsebina, ki jo pričakujete, dejansko dešifrirala, namesto da bi zaupali, da se je klic nalaganja vrnil
Česa HotPDF ne zagotavlja
HotPDF zagotavlja, da se njegov lasten zapisovalec in bralnik strinjata bajt za bajtom, in gradi ovojnice, ki sledijo navedenim strukturam CMS. Ne zagotavlja, da vsak pregledovalnik PDF odpre vsako kombinacijo. Podpora za prenos ključev RSA-OAEP in za prejemnike X25519 ali X448 se razlikuje med bralniki in različicami, rezultatov združljivosti za te kombinacije pa nismo objavili. Če se mora dokument odpreti v določenem pregledovalniku, šifrirajte preskusno datoteko za preskusni certifikat istega tipa ključa in jo tam odprite, preden se zavežete na shemo. Dovoljenja, ki jih nosi ovojnica, ostanejo politika, ki jo skladna programska oprema spoštuje, točno tako kot pri šifriranju z geslom. Tudi kakovost semena je vaša odgovornost: AESGenerateRandomBytes je tam za to delo, HotPDF pa izbriše svojo kopijo semena, ko je ključ datoteke izpeljan. Če potrebujete, da niz, tok ali priloga uporabi drugačen kripto filter, vodnik o politiki kripto filtrov za StmF, StrF in EFF pokaže, katera imena filtrov sprejema upravljalnik z javnimi ključi
Šifriranje s certifikati, ovojnice prejemnikov RSA-OAEP in ECDH ter nalaganje zasebnih ključev vsi prihajajo v komponento HotPDF Delphi PDF, skupaj s šifriranjem z gesli, digitalnimi podpisi in preostankom orodjarni ISO 32000 za Delphi in C++Builder