HotPDF salaa PDF:n tietyille sertifikaatinhaltijoille ISO 32000:n public-key security handlerin kautta: EnablePubKeyEncryption ottaa 20 tavun satunnaisen siemenen, ja jokainen vastaanottaja saa oman CMS-kirjekuorensa, jonka rakentaa AddPubKeyRecipientCertificate RSA-avaimille (RSA-OAEP-avaintensiirto) tai AddPubKeyAgreementRecipientWithSecret elliptisen käyrän avaimille (ECDH kurveilla P-256, P-384, P-521, X25519 tai X448). Kukaan ei jaa salasanaa; se, jolla on vastaava yksityinen avain, avaa tiedoston
Käyttötapaus on aina jonkinlainen versio samasta tarinasta. Neljännesvuosittainen auditointipaketti menee kolmelle ulkopuoliselle tarkastajalle, lakiosasto haluaa jokaisen lukevan sen, vain yksi heistä saa tulostaa sen, eikä kukaan halua salasanan lojuvan sähköpostiketjussa liitteen vieressä. Salasanalla salaus ei osaa ilmaista tuota. Sertifikaattisalaus osaa, koska jokainen vastaanottaja avaa asiakirjan avaimella, jota hän jo pitää hallussaan, ja jokainen vastaanottaja voi kantaa erilaista käyttöoikeusjoukkoa oman kirjekuorensa sisällä
Miten sertifikaattipohjainen PDF-salaus eroaa salasanasta?
Public-key-salattu PDF johdtaa tiedostoavaimensa satunnaisesta siemenestä plus jokaisen vastaanottajakirjekuoren täsmällisistä tavuista, ei mistään siitä, mitä ihminen näppäilee. Käsittelijä on kuvattu standardissa ISO 32000-1 §7.6.4 (§7.6.5 standardissa ISO 32000-2), ja kirjekuoret ovat RFC 5652:n mukaisia CMS-EnvelopedData-rakenteita. HotPDF kirjoittaa /Filter /Adobe.PubSecin /SubFilter /adbe.pkcs7.s5llä; AES-256:lle se tarkoittaa /V 5-arvoa ja /DefaultCryptFilter-merkintää /CFin alla /CFM /AESV3llä, ja /Recipients-taulukko asuu kyseisen crypt filterin sisällä. Jokainen kirjekuori salaa 24 tavua: 20 tavun siemenen, jota seuraa kyseisen vastaanottajan 32-bittinen käyttöoikeussana. Salaussanakirjan /P-arvo on vain paikanpitäjä, sillä oikeat käyttöoikeudet matkustavat jokaisen kirjekuoren sisällä. Latausaikana lukija purkaa yhden kirjekuoren, saa siemenen takaisin ja tiivistää siemenen yhdessä jokaisen kirjekuoren kanssa /Recipients-järjestyksessä (SHA-256 AES-256:lle, SHA-1 vanhemmille salaimille) rakentaakseen tiedostoavaimen uudelleen. Jos harkitset vielä tämän mallin ja tavallisten salasanojen välillä, opas AES-256-salasanalauksesta ja käyttöoikeuslipuista kattaa kompromissin toisen puolen
RSA-vastaanottajien kirjoittaminen EnablePubKeyEncryptionilla
RSA-sertifikaateille kutsu EnablePubKeyEncryptionia arvolla aes256 ja sitten AddPubKeyRecipientCertificateia kerran per DER-koodattu sertifikaatti ennen BeginDocia. Apuri rakentaa RSAES-OAEP-kirjekuoren prosessin sisällä THPDFRSAOAEPHash-arvoilla OAEP-tiivistykselle ja MGF1-tiivistykselle (rohSHA256, rohSHA384 tai rohSHA512), ja se salaa kirjekuoren sisällön AES-256-CBC:llä
uses
System.SysUtils, System.IOUtils, HPDFDoc, HPDFCrypt, HPDFRSA;
procedure WriteAuditPack(const OutFile: string);
var
Pdf: THotPDF;
Seed: AnsiString;
begin
SetLength(Seed, 20); // täsmälleen 20 tavua, myös AES-256:lle
AESGenerateRandomBytes(@Seed[1], Length(Seed));
Pdf := THotPDF.Create(nil);
try
Pdf.AutoLaunch := False;
Pdf.FileName := OutFile;
Pdf.EnablePubKeyEncryption(Seed, aes256, True); // oletusavaintyyppi on aes128
// Tarkastaja A saa tulostaa; tarkastaja B saa vain lukea ja poimia
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;
Kolme yksityiskohtaa kyseisessä listauksessa kantavat rakennetta. Ensinnäkin siemenen pituus on lukittu 20 tavuun kaikilla avaintyypeillä, AES-256 mukaan lukien; EnablePubKeyEncryption nostaa poikkeuksen muista pituuksista. Toiseksi EnablePubKeyEncryption olettaa aes128in, ja molemmat sertifikaattiapurit kieltäytyvät toimimasta, ellei avaintyyppi ole aes256, joten toisen argumentin unohtaminen tuottaa poikkeuksen ”certificate envelopes require aes256”. Vanhat salaimet (k40, k128, aes128) toimivat yhä, mutta vain AddPubKeyRecipientin kautta muualla rakentamallasi kirjekuorella. Kolmanneksi AES-256 public-key-salaus on PDF 2.0:n ominaisuus, joten HotPDF nostaa asiakirjan version 2.0:aan automaattisesti. Kun StrictVersionLock on asetettu matalammalle versiolle, EnablePubKeyEncryption palaa laukaisematta mitään, ja virhe ilmenee vasta seuraavalla rivillä muodossa ”call EnablePubKeyEncryption first”. Salauksen vaihtaminen inkrementaalisen päivityksen aikana nostaa EInvalidOpExceptionin suoraan
ECDH-vastaanottajien lisääminen: P-256, P-384, P-521, X25519 ja X448
Elliptisen käyrän sertifikaateille AddPubKeyAgreementRecipientWithSecret kirjoittaa CMS-avainsopimusvastaanottajan (KeyAgreeRecipientInfo, RFC 5753:n KARI-rakenne, RFC 8418:n X25519- ja X448-profiililla) ja laskee ECDH:n jaetun salaisuuden prosessin sisällä. Valitset käyrän THPDFPubKeyAgreementScheme-arvolla: pkasECDHP256, pkasECDHP384, pkasECDHP521, pkasX25519 tai pkasX448. Kaavan on täsmättävä sertifikaatin avaimeen, tai kutsu nostaa poikkeuksen ”Certificate key does not match the requested agreement scheme”. Konepellin alla jokainen kirjekuori saa tuoreen satunnaisen 32 tavun UKM:n, avainta salaavan avaimen, joka johdetaan stdDH KDF:llä (SHA-256 P-256:lle ja X25519:lle, SHA-384 P-384:lle, SHA-512 P-521:lle ja X448:lle), ja RFC 3394:n mukaisen AES-256-avainkääreen. Itse jaettu salaisuus tulee puhtaasta Pascal-käyräkoodista, ilman mukana olevaa alustan kryptopalveluntarjoajaa; artikkeli puhtaassa Pascal-koodissa toteutetusta NIST-käyräaritmetiikasta kertoo, miten kyseinen kerros rakennettiin ja varmennettiin. Montgomery-käyrille koko kertakäyttöinen avainpari voidaan tuottaa paikallisesti:
uses
System.SysUtils, System.IOUtils, HPDFDoc, HPDFCrypt, HPDFPubSec,
HPDFKeyAgreement;
procedure AddLegalRecipient(Pdf: THotPDF);
var
Scalar, OriginatorPublic: TBytes;
begin
// Tuore kertakäyttöinen skalaari jokaista kirjekuorta kohden; clampaus tapahtuu tikapuun sisällä
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: merkitsevä vain NIST-käyrille
finally
HPDFSecureClearBytes(Scalar);
end;
end;
NIST-käyrät vaativat kutsujalta enemmän. HotPDF toimittaa public-key-apurit vain X25519:lle ja X448:lle (HPDFX25519PublicFromScalar, HPDFX448PublicFromScalar), joten P-256:lle, P-384:lle ja P-521:lle tuotat kertakäyttöisen avainparin omilla työkaluillasi ja välität big-endian-skalaarin, jonka pituus on täsmälleen kentän koko (32, 48 tai 66 tavua), plus vastaavan pakkamattoman 0x04||X||Y-pisteen OriginatorPublicKeyina. HotPDF valido vastaanottajapisteen käyräyhtälöä vasten, mutta se ei voi tarkistaa, että alkuperäisen julkinen avaimesi oikeasti kuuluu skalaarillesi. Erimittaiset puoliskot tuottavat silti täydellisen hyvin muodostuneen kirjekuoren, jota mikään vastaanottaja ei voi avata, ja siksi edestakainen lataus kuuluu testisarjaasi, eikä pelkkä tiedostokoon tarkistus riitä
Miksi /Recipients-järjestyksellä on väliä?
/Recipients-taulukon järjestyksellä on väliä, koska tiedostoavain on tiivistelmä siemenestä ja jokaisesta kirjekuoresta taulukkojärjestyksessä, joten kirjoittajan ja lukijan on tiivistettävä samat tavut samassa järjestyksessä. HotPDF pitää kirjekuoret siinä järjestyksessä kuin lisäät ne ja kirjoittaa ne muuttumattomina, eli voit lisätä vastaanottajia missä järjestyksessä tahansa, mutta mikään myöhempi vaihe ei saa järjestää, uudelleenkoodata tai siistiä kyseistä taulukkoa. Useimmat alueen oikeista bugeista olivat variatio tuosta teemasta, jossa kaksi puolta tiivistivät hieman eri tavut:
- Dynaamisten taulukoiden tallentaminen
TListiinAddilla pitää kiinni vain raakaa osoitinta, kun taas viitemäärä jää paikalliselle muuttujalle. SeuraavaSetLengthvapauttaa puskurin ja voi käyttää sen uudelleen, joten jokainen lokero päätyi aliastamaan viimeistä kirjekuorta ja monivastaanottajaiset tiedostot johdattivat väärän avaimen. Korjaus on tallentaa omistettu kopio muodossaList.Add(Pointer(System.Copy(Bytes))) - Kirjekuoren purku jäsentää DER:n paikallaan, ja avainten palautusvaihe tiisti alun perin samat elossa olevat taulukot. Lukija ottaa nyt koskemattomat kopiot jokaisesta kirjekuoresta ennen kuin mikään purku koskee niitä, ja tiivistelmä ajetaan kopioiden yli
- Binäärinen DER, joka kulkee Unicode-
TStringListin läpi, saa tavunsa$80sta ylöspäin uudelleenkoodatuksi koodisivun toimesta, joten HotPDF tallentaa kirjekuoret heksatekstinä sisäisesti - Salatut ja binääriset merkkijonot on kirjoitettava heksamerkkijonoina. Literaalimerkkijonoon sovelletaan rivinlopun normalisointia, jossa CR, LF ja CRLF muuttuvat kaikki yhdeksi LF:ksi (ISO 32000-1 §7.3.4.2), ja se kirjoittaa salatekstin uudelleen hiljaa. HotPDF tuottaa jokaisen
/Recipients-merkinnän heksamerkkijonona ja vapauttaa sen merkkijonosalaamiselta, sillä jokainen lukija tarvitsee kirjekuoret ennen kuin sillä on mitään avainta - DER-
BIT STRINGin ensimmäinen tavu laskee käyttämättömiä bittejä ja sen on oltava nolla tavutasattujen avainten kohdalla. Alustamatta jättäminenSetLengthin jälkeen kirjoitti pinossa sattuneen, ja tiukka purkaja hylkäsi alkuperäisen avaimen, joten tiedosto saattoi satunnaisesti epäonnistua avautumaan täsmälleen sillä avaimella, jolle se oli kirjoitettu - Kun sama avain ei silti pysty purkamaan, vertaa kerros kerrokselta: tiedostoavain, sitten salatekstin etuliite (IV), sitten objektiavain, sitten selväkielinen teksti. Bugi asuu heti ensimmäisen eriävän kerroksen jälkeen
Miten avaat sertifikaatilla salatun PDF:n yksityisellä avaimella?
Avataksesi sertifikaatilla salatun PDF:n rekisteröi yksityisen avaimen materiaali ennen LoadFromFileia, koska HotPDF palauttaa tiedostoavaimen rakenteellisen kävelyn aikana. Sijoita HPDFParsePFXilla jäsennetty RSA- tai EC-avain PubSecKeyMaterialiin, lisää lisää RSA-avaimia AddPubSecKeyMaterialilla ja rekisteröi raakat ECDH-skalaarit kutsulla AddPubSecAgreementKeyMaterial(CurveOID, PrivateScalar, OwnPublicPoint) käyttäen vakioita HPDFOIDX25519, HPDFOIDX448, HPDFOIDECP256, HPDFOIDECP384 tai HPDFOIDECP521. NIST-käyrät vaativat vastaanottajan oman pakkamattoman julkisen pisteen; Montgomery-käyrät ohittavat sen
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);
// Valinnainen: poimi kirjekuori suoraan sen sijaan että kokeilet niitä kaikkia
Reader.PubSecRecipientQuery :=
function(Context: Pointer; RecipientCount: Integer): Integer
begin
Result := -1; // -1 = kokeile jokaista kirjekuorta järjestyksessä
end;
Reader.LoadFromFile('audit-pack.pdf', '');
Writeln('Pages: ', Reader.GetLoadedPageCount);
finally
Reader.Free;
end;
end;
Ilman takaisinkutsua HotPDF kokeilee jokaista kirjekuorta jokaista rekisteröityä avainta vastaan: ensin ensisijainen avain, sitten jokainen lisätty RSA-avain, sitten EC-materiaali. PubSecRecipientQuery vastaanottaa kirjekuorten määrän ja palauttaa nollapohjaisen indeksin tai -1:n, ja taulukon ulkopuolinen indeksi nostaa poikkeuksen sen sijaan että se katkaistaisiin rajoille. Huomaa, että AddPubSecKeyMaterial hyväksyy vain RSA-materiaalin (se vaatii moduluksen ja yksityisen eksponentin), joten EC-avaimet kuuluvat PubSecKeyMaterialiin tai AddPubSecAgreementKeyMaterialiin. Kun mikään avain ei pysty purkamaan mitään kirjekuorta, palautusvaihe palaa ilman tiedostoavainta poikkeuksen sijaan, joten varmista, että odottamasi sisältö oikeasti purkautui sen sijaan että luottaisit siihen, että latauskutsu palasi
Mitä HotPDF ei takaa
HotPDF takaa, että sen oma kirjoittaja ja lukija ovat yhtä mieltä tavu tavulta, ja se rakentaa kirjekuoret, jotka noudattavat yllä siteerattuja CMS-rakenteita. Se ei takaa, että jokainen PDF-katselin avaa jokaisen yhdistelmän. Tuki RSA-OAEP-avaintensiirrolle ja X25519- tai X448-vastaanottajille vaihtelee lukijoiden ja versioiden välillä, emmekä ole julkaisseet yhteensopivuustuloksia niille yhdistelmille. Jos asiakirjan on avauduttava tietyssä katselimessa, salaa testitiedosto saman avaintyypin testisertifikaatille ja avaa se siellä ennen kuin sitoudut kaavaan. Kirjekuorissa kulkevat käyttöoikeudet pysyvät politiikkana, jota standardinmukainen ohjelmisto kunnioittaa, täsmälleen kuten salasanalauksessakin. Siemenen laatu on myös sinun vastuullasi: AESGenerateRandomBytes on olemassa juuri tuohon työhön, ja HotPDF pyyhkii oman siemenkopionsa heti, kun tiedostoavain on johdettu. Jos tarvitset lisäksi merkkijonon, streamin tai liitteen käyttävän toista crypt filteriä, opas crypt filter -politiikasta StmF, StrF ja EFF näyttää, mitkä suodattimien nimet public-key-käsittelijä hyväksyy
Sertifikaattisalaus, RSA-OAEP- ja ECDH-vastaanottajakirjekuoret sekä yksityisen avaimen lataaminen toimitetaan kaikki HotPDF Delphi PDF -komponentissa, yhdessä salasanalauksen, digitaalisten allekirjoitusten ja muun ISO 32000 -työkaluvalikoiman kanssa Delphille ja C++Builderille