Techninis straipsnis

Pokvantinis ir EdDSA PDF pasirašymas su HotPDF Delphi

HotPDF tikrina ML-DSA-44, ML-DSA-65, ML-DSA-87, Ed25519 ir Ed448 CMS parašus įkeltuose PDF dokumentuose, ir jis pasirašo per įkišamus paslaugų teikėjus, todėl privatūs raktai niekada neturi gyventi jūsų Delphi procese. Antroji pusė yra ta dalis, kurios dauguma komandų pirmiausia reikia. Aparatinis raktas, nuotolinė pasirašymo paslauga ir nacionalinė eID kortelė visi atsisako atiduoti raktą, o kol pasirašymo konvejeris nėra atskirtas nuo raktų saugyklos, nė vienas iš jų išvis negali būti naudojamas

To atskyrimas ir yra THPDFSignatureProvider tikslas. HotPDF pasilaiko dalis, kurias turėtų valdyti — CMS analizė, SignedData kūrimas, /ByteRange išdėstymas — ir perduoda vieną operaciją, kurios ji negali valdyti, tai yra santraukos pavertimą parašu raktu, kurio jai neleidžiama matyti. Viskas žemiau išplaukia iš šio padalijimo

Kodėl tinkamas ML-DSA parašas nepraeina tikrinimo?

Nes HotPDF atsisako ML-DSA įkeltame dokumente, kuris nedeklaruoja tam plėtinio. ML-DSA — gardelinė parašų schema, standartizuota kaip FIPS 204, ir priežastis, kodėl žmonės sako „pokvantinis PDF“ — dar neturi ISO 32000-2 registracijos. PDF, nešantis jį, naudoja algoritmą, kurio bazinis standartas nepavadina, o failas, tyliai naudojantis nepavadintą algoritmą, yra failas, kurio verdikto niekas kitas negali atkartoti

Tad HotPDF padaro deklaraciją aiškią. EnsureMLDSAExtensions pakelia dokumentą į PDF 2.0, kai leidžiama, ir įrašo /Extensions /HotPDF << /BaseVersion /2.0 /ExtensionLevel 1 >> į katalogą. Skaitymo pusėje LoadedDocumentDeclaresMLDSAExtension praneša, ar ta deklaracija išgyveno, o VerifyLoadedSignatureWithOptions taiko tą patį testą prieš gerbdamas Options.AllowMLDSA. Nustatykite vėliavėlę nedeklaruotame dokumente, ir ji lieka išjungta — parinktis gali atleisti politiką, bet niekada ne struktūrinį reikalavimą

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'contract-pq.pdf';
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 720, 0, 'Supply agreement 2026-114');
    Pdf.EnsureMLDSAExtensions;   // declare before the signature is written
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Kvieskite tai prieš išsaugant, o ne po. Deklaracija yra pasirašyto baitų intervalo dalis, o katalogas, pataisytas vėliau, yra arba nepasirašytas pasirašyto failo pakeitimas, arba antroji redakcija, kurią validatorius praneš kaip modifikaciją

Trios algoritmų šeimos, vienas tikrinimo įėjimo taškas

Visos trys šeimos ateina per VerifyLoadedSignatureWithOptions, kuris priima parašo indeksą, šaltinio srautą, THPDFCMSVerifyOptions įrašą ir išėjimo parametrą parašo detalėms. Įrašas turi lygiai tris laukus, ir kiekvienas atsako į klausimą, anksčiau reikalavusį perkompiliavimo

SignatureProvider pakeičia jūsų savąją paslaugų teikėją į įmontuotą platformos. OpenSSLLibraryPath parenka OpenSSL 3 biblioteką, kuri ir tiekia gryno režimo Ed25519 ir Ed448 tikrinimą, kurio Windows CNG ne visur siūlo. AllowMLDSA įsijungia į gardelinius algoritmus, sąlygotai aukščiau esančio plėtinio tikrinimo. Tikslus algoritmo OID, kuris buvo atpažintas, grįžta THPDFSignatureInfo.SignatureAlgorithmOID, todėl audito žurnalas gali užrašyti, kas buvo tikrintas, o ne kas buvo prašyta

var
  Opts: THPDFCMSVerifyOptions;
  Info: THPDFSignatureInfo;
  Status: THPDFSignatureVerifyStatus;
  Src: TFileStream;
begin
  Opts := THPDFCMSVerifyOptions.Default;
  Opts.OpenSSLLibraryPath := 'C:\openssl3\libcrypto-3-x64.dll';
  Opts.AllowMLDSA := Pdf.LoadedDocumentDeclaresMLDSAExtension;
  Src := TFileStream.Create('contract-pq.pdf', fmOpenRead or fmShareDenyWrite);
  try
    Status := Pdf.VerifyLoadedSignatureWithOptions(0, Src, Opts, Info);
    if Status = svValid then
      Memo1.Lines.Add('signed with OID ' + string(Info.SignatureAlgorithmOID));
  finally
    Src.Free;
  end;
end;

Ed25519 ir Ed448 nereikalauja plėtinio deklaracijos, nes ISO 32000-2 juos jau priima. Jiems reikia paslaugų teikėjo, kuris juos įgyvendina, kas daugumoje Windows diegimų reiškia OpenSSLLibraryPath nukreipimą į biblioteką, kurią pateikiate ir valdote, o ne į tai, kas atsitiktinai yra kompiuteryje

Ką iš tikrųjų žada pasirašymo paslaugų teikėjas?

Paslaugų teikėjas žada vieną dalyką: gavęs užklausą, grąžinti būseną ir, kai pasirašoma, baitus. THPDFSignatureProviderRequest neša algoritmą ir jo OID, santraukos OID, PSS druskos ilgį, ar įvestis yra pranešimas, ar jau apskaičiuota santrauka, pačią įvestį, viešąjį raktą ar sertifikatą, rakto identifikatorių ir operacijos identifikatorių. Nieko tame įraše nėra HotPDF-specifinio — tai yra žodynas, kurį rakto tvarkyklė ar pasirašymo paslauga jau kalba

Su biblioteka pristatomi trys įgyvendinimai. THPDFCallbackSignatureProvider apgaubia anoniminius metodus, kas yra trumpiausias kelias nuo esamo vidinio pasirašymo pobūdžio iki veikiančio PDF parašo. THPDFRemoteSignatureProvider apgaubia transportavimo atgalinį iškvietimą su pakartojimo limitu, atšaukimo registru ir ribomis įvesčiai bei parašo dydžiui, kad pakibęs HSM negalėtų tapti pakibusia programa. THPDFPKCS11SignatureProvider serijuoja RSA operacijas prieš kviesiančiojo valdomą, jau autentifikuotą PKCS#11 sesiją ir privataus rakto deskriptorių — HotPDF niekada neprisijungia, niekano nemato PIN ir niekada neuždaro sesijos, kurios neatidarė

var
  Provider: THPDFRemoteSignatureProvider;
begin
  Provider := THPDFRemoteSignatureProvider.Create(
    function(const Req: THPDFSignatureProviderRequest; Attempt: Integer;
      out Signature: TBytes): THPDFSignatureProviderStatus
    begin
      // POST Req.Input to the signing service; Req.KeyIdentifier selects the key
      if PostToSigningService(Req.KeyIdentifier, Req.Input, Signature) then
        Result := spsValid
      else
        Result := spsProviderError;
    end,
    3,          // RetryLimit
    1048576,    // MaxInputBytes
    65536);     // MaxSignatureBytes
  try
    // hand Provider to the signing call
  finally
    Provider.Free;
  end;
end;

Kodėl būsenos išvardijimas turi šešias reikšmes vietoj boolean

THPDFSignatureProviderStatus atskiria spsValid, spsInvalid, spsUnsupported, spsMalformed, spsProviderError ir spsCancelled, ir jų suliejimas kainuoja gebėjimą elgtis teisingai. Kriptografiškai neteisingas parašas (spsInvalid) yra saugumo įvykis. Algoritmas, kurio paslaugų teikėjas neįgyvendina (spsUnsupported), yra diegimo spraga. Transportavimo triktis (spsProviderError) verta pakartoti, o naudotojo atšauktas rakto raginimas (spsCancelled) išvis neverta pakartoti

Taisyklė pasirašymui yra siaura: pasirašymo paslaugų teikėjas grąžina spsValid tik su netuščiu parašu. Tikrinimo paslaugų teikėjai grąžina spsValid arba spsInvalid, o kitos keturios lieka atskiros abiem keliais. Jei rašote paslaugų teikėją, pasipriešinkite pagundai viską, ko neatpažįstate, priimti kaip spsInvalid — tai paverčia trūkstamą DLL pranešimu, kad kliento parašas yra suklastotas

Kur parašas tikrai nusileidžia faile

Dvi funkcijos sujungia paslaugų teikėjus su realiais PDF baitais. HPDFCMSBuildSignedDataWithProvider stato atskirtą CMS iš dokumento SHA-256 santraukos, kas yra teisingas įėjimo taškas, kai jūsų darbo eiga santrauką apskaičiuoja kitur. HPDFCMSSignPDFStreamWithProvider pasirašo esamą parašo vietą PDF sraute ir išlaiko standartinį /ByteRange konvejerį, kas yra teisingas įėjimo taškas, kai HotPDF pats išdėstė vietą

To konvejerio išlaikymas svarbesnis, nei skamba. /ByteRange konvencija — du intervalai, praleidžiantys šešioliktainio parašo langą — yra tai, ką validatorius pirmiausia tikrina, o iš paslaugų teikėjo grįstas kelias, ją perrašęs, sulaužytų PAdES atitiktį, neatsižvelgiant į tai, koks tvirtas būtų kriptografija. HotPDF išlaiko išdėstymą tokį patį kaip įmontuotas pasirašymo kelias, todėl dokumentas, pasirašytas per PKCS#11 raktą, patikrinamas su tuo pačiu parašo tikrinimo kodu kaip ir pasirašytas iš PFX failo. Dėl profilio taisyklių, esančių virš algoritmo parinkties, žr. PAdES bazinių parašų Delphi aprašymą, o dėl ECDSA-specifinių kodavimo spąstų, anksčiau už šio paslaugų teikėjo modelio, — ECDSA CMS tikrinimo ir P1363 parašų formatų pastabas

Migracijos tvarka, kuri nepaliekajūsų dokumentų likimo valiai

Pokvantinis pasirengimas yra grafiko problema, o ne jungiklis. Beveik joks šiandien naudojamas PDF žiūryklė netikrina ML-DSA, todėl dokumentas, pasirašytas tik juo, iš skaitytojo pusės yra dokumentas su nepatikrinamu parašu. Tvarka, kuri išgyvena kontaktą su realiais archyvais, yra: laikyti RSA arba ECDSA kaip parašą, kurį vertins validatorius, pridėti plėtinio deklaraciją ir antrą ML-DSA parašą ten, kur politika reikalauja kvantui atsparių įrodymų, ir perkelti pagrindinį parašą tik tada, kai vartojančios sistemos bus pasivijusios

Ką HotPDF jums šiandien duoda, tai gebėjimą rašyti ir tikrinti abu, iš to paties kodo, su algoritmu sąžiningai užrašytu faile ir tikrinimo rezultate. HotPDF yra natyvus VCL PDF komponentas, skirtas Delphi ir C++Builder, be išorinės PDF vykdymo aplinkos, todėl pasirašymo ir tikrinimo keliai keliauja jūsų vykdomajame faile, o ne šalia jo — žr. HotPDF Delphi PDF komponento puslapį dėl pilno savybių sąrašo ir bandomosios versijos atsisiuntimo