Техническа статия

Пост-квантово и EdDSA подписване на PDF с HotPDF в Delphi

HotPDF верифицира ML-DSA-44, ML-DSA-65, ML-DSA-87, Ed25519 и Ed448 CMS подписи в заредени PDF документи и подписва чрез включвани доставчици, така че частният ключ никога не трябва да живее вътре във вашия Delphi процес. Втората половина е частта, от която повечето екипи се нуждаят най-напред. Хардуерен токен, отдалечена услуга за подписване и национална eID карта всички отказват да предадат ключ, и докато пайплайнът за подписване не бъде отделен от хранилището на ключове, нито един от тях не може да се използва изобщо

Това разделяне е смисълът на THPDFSignatureProvider. HotPDF запазва частите, които трябва да притежава — синтактичен разбор на CMS, изграждане на SignedData, подредба на /ByteRange — и делегира еднаствената операция, която не може да притежава, а именно превръщането на дайджест в подпис с ключ, който няма право да види. Всичко по-долу следва от това разделение

Защо валиден ML-DSA подпис не минава проверка?

Защото HotPDF отказва ML-DSA върху зареден документ, който не декларира разширението за него. ML-DSA — решетъчната схема за подпис, стандартизирана като FIPS 204, и причината хората да казват «пост-квантов PDF» — все още няма регистрация в ISO 32000-2. PDF, който носи такъв, използва алгоритъм, който базовият стандарт не назовава, а файл, който мълчаливо използва не назован алгоритъм, е файл, чиято присъда не може да бъде възпроизведена от никой друг

Така че HotPDF прави претенцията изрична. EnsureMLDSAExtensions повишава документа до PDF 2.0, когато е разрешено, и записва /Extensions /HotPDF << /BaseVersion /2.0 /ExtensionLevel 1 >> в каталога. От страна на четенето LoadedDocumentDeclaresMLDSAExtension отчита дали тази декларация е оцеляла, а VerifyLoadedSignatureWithOptions прилага същия тест, преди да зачете Options.AllowMLDSA. Задайте флага върху недеклариран документ и той остава изключен — опцията може да разхлаби политика, но никога структурното изискване

HotPDF огражда PDF верификацията с ML-DSA зад изрична декларация за разширение в Catalog, записвана от EnsureMLDSAExtensions преди записа на документа, и отказва решетъчни алгоритми на недекларирани файлове, дори когато AllowMLDSA е зададено
HotPDF допуска ML-DSA само когато Catalog все още декларира разширението, което поддържа post-quantum присъдата възпроизводима, а извикването на EnsureMLDSAExtensions преди EndDoc поставя декларацията вътре в подписания байтов диапазон, а не след него
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;   // декларирайте преди записването на подписа
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Извикайте го преди запазване, не след него. Декларацията е част от подписания байтов диапазон, а каталог, закърпен след това, е или неподписана промяна върху подписан файл, или втора ревизия, която валидатор ще отчита като модификация

Три семейства алгоритми, една точка за верификация

И трите семейства пристигат през VerifyLoadedSignatureWithOptions, който приема индекс на подпис, изходния поток, запис THPDFCMSVerifyOptions и out параметър за детайлите на подписа. Записът има точно три полета и всяко отговаря на въпрос, който по-рано изискваше прездравяване

SignatureProvider замества вашия собствен доставчик с вградения платформен. OpenSSLLibraryPath избира библиотека OpenSSL 3, която е това, което доставя верификация на Ed25519 и Ed448 в чист режим, каквото Windows CNG не предлага навсякъде. AllowMLDSA включва решетъчните алгоритми, при подчинение на горната проверка за разширение. Точният OID на разпознатия алгоритъм се връща в THPDFSignatureInfo.SignatureAlgorithmOID, така че одитен лог може да запише какво е било верифицирано, а не какво е било заявено

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 и Ed448 не се нуждаят от декларация за разширение, защото ISO 32000-2 вече ги допуска. Нуждаят се обаче от доставчик, който ги реализира, което при повечето Windows разгръщания означава да насочите OpenSSLLibraryPath към библиотека, която вие доставяте и контролирате, вместо към каквото и да е на машината

Какво всъщност обещава доставчикът за подписване?

Доставчикът обещава едно нещо: при заявка връща статус и, при подписване, байтове. THPDFSignatureProviderRequest носи алгоритъма и неговия OID, OID на дайджеста, дължината на PSS солта, дали входът е съобщение или вече пресметнат дайджест, самия вход, публичния ключ или сертификат, идентификатор на ключ и идентификатор на операция. Нищо в този запис не е специфично за HotPDF — това е речникът, който драйверът на токен или услугата за подписване вече говори

Три реализации се доставят с библиотеката. THPDFCallbackSignatureProvider обвива анонимни методи, което е най-краткият път от съществуваща вътрешна рутина за подписване до работещ PDF подпис. THPDFRemoteSignatureProvider обвива транспортен callback с лимит за повторни опити, регистър за отменяне и граници за размер на входа и подписа, така че заклещен HSM не може да стане заклещено приложение. THPDFPKCS11SignatureProvider сериализира RSA операции срещу притежавана от извикващия, вече удостоверена PKCS#11 сесия и манипулатор на частен ключ — HotPDF никога не вписва, никога не вижда PIN и никога не затваря сесия, която не е отварял

Плъгваемите доставчици на подписи в HotPDF обменят един прост запис-заявка и връщат статус с шест стойности — показано за имплементациите с callback, отдалечена и PKCS#11 — докато частният ключ така и не влиза в Delphi процеса
Всичките три изпращани имплементации говорят един и същ запис за заявка и отговарят с един и същ статус от шест стойности, което позволява на хардуерни токени, отдалечени HSM услуги и национални eID карти да продължат да държат ключовете си, вместо да ги експортират
var
  Provider: THPDFRemoteSignatureProvider;
begin
  Provider := THPDFRemoteSignatureProvider.Create(
    function(const Req: THPDFSignatureProviderRequest; Attempt: Integer;
      out Signature: TBytes): THPDFSignatureProviderStatus
    begin
      // Изпратете Req.Input към услугата за подписване; Req.KeyIdentifier избира ключа
      if PostToSigningService(Req.KeyIdentifier, Req.Input, Signature) then
        Result := spsValid
      else
        Result := spsProviderError;
    end,
    3,          // RetryLimit
    1048576,    // MaxInputBytes
    65536);     // MaxSignatureBytes
  try
    // подайте Provider към извикването за подписване
  finally
    Provider.Free;
  end;
end;

Защо статусният enum има шест стойности вместо булев

THPDFSignatureProviderStatus разграничава spsValid, spsInvalid, spsUnsupported, spsMalformed, spsProviderError и spsCancelled, а свиването им в едно ви струва способността да действате правилно. Подпис, който е криптографски грешен (spsInvalid), е събитие за сигурност. Алгоритъм, който доставчикът не реализира (spsUnsupported), е пролука в разгръщането. Транспортен отказ (spsProviderError) си струва да се повтори, а подсказване за токен, отменено от потребителя (spsCancelled), не си струва да се повтаря изобщо

Правилото за подписване е тясно: доставчик за подписване връща spsValid само с непразен подпис. Доставчиците за верификация връщат spsValid или spsInvalid, а останалите четири остават разграничени и по двата пътя. Ако пишете доставчик, устояйте на изкушението да изобразявате всичко, което не разпознавате, като spsInvalid — това превръща липсваща DLL в доклад, че подписът на клиента е фалшифициран

Къде подписът реално каца във файла

Две функции свързват доставчиците с реални PDF байтове. HPDFCMSBuildSignedDataWithProvider изгражда отделен CMS от дайджест SHA-256 на документ, което е правилната входна точка, когато работният ви процес изчислява дайджеста другаде. HPDFCMSSignPDFStreamWithProvider подписва съществуващо место за подпис в PDF поток и запазва стандартния пайплайн /ByteRange, което е правилната входна точка, когато HotPDF сам е подредил местото

Запазването на този пайплайн има повече значение, отколкото звучи. Конвенцията /ByteRange — два диапазона, които прескачат hex прозореца на подписа — е това, което всеки валидатор проверява първо, а базиран на доставчик път, който да го презапише, би счупил съответствие с PAdES, без значение колко здрава е криптографията. HotPDF запазва подредбата идентична с вградения път за подписване, така че документ, подписан през PKCS#11 токен, се верифицира със същия код за верификация на подписи като подписан от PFX файл. За правилата на профила, които сядат над избора на алгоритъм, вижте ръководството за PAdES базови подписи в Delphi, а за ECDSA-специфичните капани на кодирането, които предхождат този модел с доставчици — записките за ECDSA CMS верификация и P1363 формати на подписи

Две входни точки за подписване с доставчик в HotPDF — HPDFCMSBuildSignedDataWithProvider за външно изчислен дайджест и HPDFCMSSignPDFStreamWithProvider за подреден плейсхолдър — се събират в едно CMS подреждане, запазващо обхватите /ByteRange, които валидаторите проверяват първо
Всяка от двете входни точки записва идентичната подписана форма от два покрити диапазона около hex прозореца, затова документ, подписан от доставчик, достига всеки валидатор точно като подписан от PFX файл

Ред на миграция, който не оставя документите ви изостанали

Пост-квантовата готовност е проблем на графика, а не превключвател. Почти никой разгърнат PDF четец не валидира ML-DSA днес, така че документ, подписан само с него, от гледна точка на четеца е документ с неверифицируем подпис. Редът, който оцелява при контакт с реални архиви, е: запазете RSA или ECDSA като подписа, който валидатор ще съди, добавете декларацията за разширение и втори ML-DSA подпис там, където политика изисква квантово-устойчиво доказателство, и преместете първичния подпис само когато консумиращите системи са настигнали

Това, което HotPDF ви дава днес, е способността да записвате и верифицирате и двете, от същия код, с алгоритъма често записан във файла и в резултата от верификацията. HotPDF е нативен VCL PDF компонент за Delphi и C++Builder без външен PDF runtime, така че пъщите за подписване и верификация се доставят вътре във вашия изпълним файл, а не до него — вижте страницата на HotPDF Delphi PDF компонент за пълния списък с функции и пробно изтегляне