Technický článek

Post-kvantové a EdDSA podepisování PDF s HotPDF v Delphi

HotPDF ověřuje CMS podpisy ML-DSA-44, ML-DSA-65, ML-DSA-87, Ed25519 a Ed448 v načtených PDF dokumentech a podepisuje přes výměnné providery, takže privátní klíč nikdy nemusí žít uvnitř vašeho procesu v Delphi. Právě ta druhá polovina je část, kterou většina týmů potřebuje jako první. Hardwarový token, vzdálená podepisovací služba i národní eID karta všechny odmítnou klíč vydat a dokud není podepisovací roura oddělena od úložiště klíčů, nelze použít žádný z nich

Toto rozdělení je smyslem THPDFSignatureProvider. HotPDF si ponechává části, které má vlastnit — parsování CMS, stavbu SignedData, rozložení /ByteRange — a deleguje jedinou operaci, kterou vlastnit nemůže: přeměnu digestu na podpis klíčem, který nesmí vidět. Vše, co následuje, vyplývá z tohoto rozdělení

Proč platný ML-DSA podpis neprojde ověřením?

Protože HotPDF odmítá ML-DSA na načteném dokumentu, který nedeklaruje příslušné rozšíření. ML-DSA — mřížkové schéma podpisu standardizované jako FIPS 204 a důvod, proč se říká „post-kvantové PDF" — zatím nemá v ISO 32000-2 žádnou registraci. PDF, které ho nese, používá algoritmus, který základní standard nejmenuje, a soubor, který potichu používá nepojmenovaný algoritmus, je soubor, jehož verdikt nedokáže předehrát nikdo jiný

HotPDF proto dělá tvrzení explicitním. EnsureMLDSAExtensions tam, kde je to povoleno, povýší dokument na PDF 2.0 a zapíše /Extensions /HotPDF << /BaseVersion /2.0 /ExtensionLevel 1 >> do katalogu. Na straně čtení hlásí LoadedDocumentDeclaresMLDSAExtension, zda deklarace přežila, a VerifyLoadedSignatureWithOptions aplikuje stejný test dříve, než bude respektovat Options.AllowMLDSA. Nastavte příznak na nedeklarovaném dokumentu a zůstane vypnutý — volba může povolit politiku, nikdy však strukturální požadavek

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;

Zavolejte ji před uložením, ne po něm. Deklarace je součástí podepsaného byte rozsahu a katalog patchovaný dodatečně je buď nepodepsanou změnou podepsaného souboru, nebo druhou revizí, kterou validátor ohlásí jako modifikaci

Tři rodiny algoritmů, jediný vstupní bod ověření

Všechny tři rodiny přicházejí přes VerifyLoadedSignatureWithOptions, který přijímá index podpisu, zdrojový stream, záznam THPDFCMSVerifyOptions a výstupní parametr pro detaily podpisu. Záznam má přesně tři pole a každé odpovídá na otázku, která dříve vyžadovala rebuild

SignatureProvider nahrazuje vestavěný platformní provider vaším vlastním. OpenSSLLibraryPath vybírá knihovnu OpenSSL 3, která dodává verifikaci Ed25519 a Ed448 v pure režimu, jež Windows CNG nenabízí všude. AllowMLDSA přistupuje k mřížkovým algoritmům s výhradou kontroly rozšíření výše. Přesný OID algoritmu, který byl rozpoznán, se vrací v THPDFSignatureInfo.SignatureAlgorithmOID, takže audit log může zaznamenat, co bylo ověřeno, nikoli co bylo vyžádáno

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 a Ed448 nepotřebují deklaraci rozšíření, protože je už ISO 32000-2 uznává. Potřebují však provider, který je implementuje, což na většině nasazení Windows znamená nasměrovat OpenSSLLibraryPath na knihovnu, kterou sami dodáváte a řídíte, nikoli na to, co se zrovna náhodou nachází v počítači

Co vlastně podepisovací provider slibuje?

Provider slibuje jedinou věc: k danému požadavku vrátit stav a při podepisování bytes. THPDFSignatureProviderRequest nese algoritmus a jeho OID, OID digestu, délku PSS soli, zda je vstup zpráva nebo už vypočtený digest, samotný vstup, veřejný klíč či certifikát, identifikátor klíče a identifikátor operace. Nic v tom záznamu není specifické pro HotPDF — je to slovník, kterým už mluví ovladač tokenu nebo podepisovací služba

S knihovnou se dodávají tři implementace. THPDFCallbackSignatureProvider obaluje anonymní metody, což je nejkratší cesta od existující interní podepisovací rutiny k funkčnímu PDF podpisu. THPDFRemoteSignatureProvider obaluje transportní callback s limitem opakování, registrací rušení a omezeními velikosti vstupu i podpisu, takže zavěšený HSM nemůže přerůst v zavěšenou aplikaci. THPDFPKCS11SignatureProvider serializuje RSA operace proti PKCS#11 sezení a handle privátního klíče, které vlastní a již autentizoval volající — HotPDF se nikdy nepřihlašuje, nikdy nevidí PIN a nikdy nezavírá sezení, které sám neotevřel

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;

Proč má stavový enum šest hodnot místo booleanu

THPDFSignatureProviderStatus rozlišuje spsValid, spsInvalid, spsUnsupported, spsMalformed, spsProviderError a spsCancelled a jejich sloučení vás stojí schopnost reagovat správně. Podpis, který je kryptograficky špatný (spsInvalid), je bezpečnostní událost. Algoritmus, který provider neimplementuje (spsUnsupported), je mezera v nasazení. Selhání transportu (spsProviderError) stojí za to zkusit znovu, kdežto uživatelem zrušený dotaz na token (spsCancelled) za to nestojí vůbec

Pravidlo pro podepisování je úzké: podepisovací provider vrací spsValid pouze s neprázdným podpisem. Verifikační providery vrací spsValid nebo spsInvalid a zbylé čtyři zůstávají na obou cestách oddělené. Pokud píšete provider, odolte pokušení mapovat cokoli nerozpoznaného na spsInvalid — to mění chybějící DLL v hlášení, že podpis zákazníka je padělaný

Kam podpis ve souboru skutečně dopadne

Dvě funkce propojují providery se skutečnými bajty PDF. HPDFCMSBuildSignedDataWithProvider staví detached CMS z digestu dokumentu SHA-256, což je správný vstupní bod, když vaše roura počítá digest jinde. HPDFCMSSignPDFStreamWithProvider podepisuje existující zástupné místo podpisu v PDF streamu a zachovává standardní pipelainu /ByteRange, což je správný vstupní bod, když HotPDF zástupné místo sám rozložil

Zachovat tu pipelainu má větší váhu, než zní. Konvence /ByteRange — dvě rozsahy, které přeskakují hex okénko podpisu — je to, co každý validátor kontroluje jako první, a cesta přes provider, která by ji přepsala, by rozbila shodu s PAdES bez ohledu na to, jak bezpečná byla kryptografie. HotPDF drží rozložení totožné s vestavěnou podepisovací cestou, takže dokument podepsaný přes PKCS#11 token projde stejným kódem pro ověření podpisu jako dokument podepsaný z PFX souboru. Pravidla profilu, která stojí nad volbou algoritmu, rozebírá průvodce baseline podpisy PAdES v Delphi a specifické encoding pasti ECDSA, které předcházely tomuto modelu providerů, poznámky k verifikaci ECDSA CMS a formátům podpisu P1363

Pořadí migrace, které nenechá vaše dokumenty na holičkách

Připravenost na post-kvantum je problém harmonogramu, ne přepínač. Téměř žádný dnes nasazený PDF prohlížeč ML-DSA nevaliduje, takže dokument podepsaný jen jím je z pohledu čtenáře dokument s neověřitelným podpisem. Pořadí, které přežije kontakt se skutečnými archivy, je: ponechte RSA nebo ECDSA jako podpis, jejž bude validátor posuzovat, přidejte deklaraci rozšíření a druhý ML-DSA podpis tam, kde politika vyžaduje kvantově odolný důkaz, a přesuňte primární podpis až ve chvíli, kdy konzumující systémy dorůstou

Co HotPDF dnes dává, je schopnost zapsat i ověřit obojí, ze stejného kódu, s algoritmem poctivě zaznamenaným v souboru i ve výsledku ověření. HotPDF je nativní VCL PDF komponenta pro Delphi a C++Builder bez externího PDF runtime, takže podepisovací i verifikační cesta žije uvnitř vašeho spustitelného souboru, nikoli vedle něj — viz stránka HotPDF Delphi PDF komponenty pro úplný seznam funkcí a zkušební stažení