A HotPDF ellenőrzi az ML-DSA-44, ML-DSA-65, ML-DSA-87, Ed25519 és Ed448 CMS-aláírásokat a betöltött PDF-dokumentumokban, és bedugható szolgáltatókon keresztül ír alá, így a privát kulcsnak sosem kell a Delphi-folyamatunkon belül élnie. Ez a második fel az a rész, amelyet a legtöbb csapat először akar. Egy hardveres token, egy távoli aláírási szolgáltatás és egy nemzeti eID-kártya mind vonakodik átadni a kulcsot, és amíg az aláíró futószalag nincs elválasztva a kulcstárolótól, egyikkel sem lehet dolgozni
Ez az elválasztás a THPDFSignatureProvider lényege. A HotPDF megtartja azokat a részeket, amelyeket birtokolnia kell — a CMS elemzése, a SignedData felépítése, a /ByteRange kiosztása —, és átruházza azt az egyetlen műveletet, amelyet nem birtokolhat: egy digest aláírássá alakítását egy olyan kulccsal, amelyet nem láthat. Minden, ami lent következik, ebből a felosztásból fakad
Miért bukik meg egy érvényes ML-DSA-aláírás ellenőrzése?
Mert a HotPDF visszautasítja az ML-DSA-t egy olyan betöltött dokumentumon, amely nem deklarálja hozzá a kiterjesztést. Az ML-DSA — a FIPS 204 szabványosította rácsos aláírási séma, és az oka annak, hogy az emberek „poszt-kvantum PDF”-ről beszélnek — még nem rendelkezik ISO 32000-2 regisztrációval. Egy olyan PDF, amely ilyet hordoz, egy olyan algoritmust használ, amelyet az alapszabvány nem nevez meg, és egy fájl, amely csendben névtelen algoritmust használ, olyan fájl, amelynek ítéletét senki más nem tudja reprodukálni
Ezért teszi a HotPDF a hivatkozást kifejezetté. Az EnsureMLDSAExtensions ahol lehetséges, PDF 2.0-ra emeli a dokumentumot, és beírja a /Extensions /HotPDF << /BaseVersion /2.0 /ExtensionLevel 1 >> elemet a katalógusba. Olvasási oldalon a LoadedDocumentDeclaresMLDSAExtension jelzi, hogy a deklaráció túlélte-e, és a VerifyLoadedSignatureWithOptions ugyanezt a tesztet alkalmazza, mielőtt figyelembe venné az Options.AllowMLDSA-t. Állítsuk be a jelzőt egy nem deklarált dokumentumon, és kikapcsolva marad — az opció csak lazíthatja a házirendet, sosem a strukturális követelményt
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;
Mentsés előtt hívjuk, nem utána. A deklaráció az aláírt bájttartomány része, és egy utólag javított katalógus vagy egy aláírt fájl aláíratlan változása, vagy egy második revízió, amelyet egy validátor módosításként jelent
Három algoritmuscsalád, egy ellenőrzési belépési pont
Mind a három család a VerifyLoadedSignatureWithOptions-ön át érkezik, amely átvesz egy aláírás-indexet, a forrásfolyamot, egy THPDFCMSVerifyOptions rekordot és egy out paramétert az aláírás részleteihez. A rekordnak pontosan három mezője van, és mindegyik egy olyan kérdésre válaszol, amely korábban újraépítést igényelt
A SignatureProvider a beépített platform szerintit saját szolgáltatóra cseréli. Az OpenSSLLibraryPath egy OpenSSL 3 könyvtárat választ, amely szolgáltatja azt a pure-mód Ed25519 és Ed448 ellenőrzést, amelyet a Windows CNG nem mindenhol kínál. Az AllowMLDSA a fenti kiterjesztés-ellenőrzés alárendelve engedélyezi a rácsos algoritmusokat. A felismert pontos algoritmus-OID a THPDFSignatureInfo.SignatureAlgorithmOID-ben jön vissza, így egy auditnapló rögzítheti, mit ellenőriztek, nem azt, mit kértek
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;
Az Ed25519 és Ed448 nem igényel kiterjesztés-deklarációt, mert az ISO 32000-2 már felveszi őket. Viszont olyan szolgáltatóra van szükségük, amely megvalósítja őket, ami a legtöbb Windows-telepítésen azt jelenti, hogy az OpenSSLLibraryPath-et egy olyan könyvtárra mutatjuk, amelyet mi szállítunk és szabályozunk, nem pedig arra, ami épp a gépen van
Mit ígér valójában egy aláíró szolgáltató?
Egy szolgáltató egy dolgot ígér: egy kérésre visszaad egy állapotot és, aláíráskor, bájtokat. A THPDFSignatureProviderRequest hordozza az algoritmust és az OID-jét, a digest OID-jét, a PSS sóhosszt, hogy a bemenet üzenet vagy már kiszámított digest-e, magát a bemenetet, a nyilvános kulcsot vagy tanúsítványt, egy kulcsazonosítót és egy művelet-azonosítót. Semmi sem ebben a rekordban HotPDF-specifikus — ez az a szókincs, amelyet egy token-meghajtó vagy egy aláírási szolgáltatás már beszél
Három megvalósítás érkezik a könyvtárral. A THPDFCallbackSignatureProvider névtelen metódusokat csomagol, ami a legrövidebb út egy meglévő házon belüli aláíró rutintól egy működő PDF-aláírásig. A THPDFRemoteSignatureProvider egy átviteli callbacket csomagol újrapróbálkozási korláttal, egy megszakításjegyzékkel és korlátokkal a bemenet és az aláírás méretén, így egy lefagyott HSM nem válik lefagyott alkalmazássá. A THPDFPKCS11SignatureProvider RSA-műveleteket sorosít egy hívó által birtokolt, már hitelesített PKCS#11-munkamenet és privátkulcs-leíró ellen — a HotPDF sosem lép be, sosem lát PIN-t, és sosem zár egy olyan munkamenetet, amelyet nem nyitott
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;
Miért hat értéke van a státuszenumnek egy boolean helyett?
A THPDFSignatureProviderStatus megkülönböztet spsValid, spsInvalid, spsUnsupported, spsMalformed, spsProviderError és spsCancelled értékeket, és ezek összevonása elveszi a helyes cselekvés képességét. Egy kriptográfiailag hibás aláírás (spsInvalid) biztonsági esemény. Egy olyan algoritmus, amelyet a szolgáltató nem valósít meg (spsUnsupported) telepítési hiány. Egy átviteli hiba (spsProviderError) érdemes újrapróbálni, egy felhasználó által megszakított token-promt (spsCancelled) egyáltalán nem érdemes
Az aláírási szabály szűk: egy aláíró szolgáltató csak nem üres aláírással ad vissza spsValid-ot. Az ellenőrző szolgáltatók spsValid vagy spsInvalid értéket adnak vissza, a többi négy mindkét úton külön marad. Ha szolgáltatót írunk, álljunk ellent annak a kísértésnek, hogy mindent, amit nem ismersz, spsInvalid-ra képezz le — ez egy hiányzó DLL-t egy olyan jelentéssé alakít, amely szerint az ügyfél alírása hamisítvány
Hol landol az aláírás valójában a fájlban
Két függvény köti össze a szolgáltatókat a valódi PDF-bájtokkal. A HPDFCMSBuildSignedDataWithProvider detached CMS-t épít egy dokumentum SHA-256 digestjéből, ami a megfelelő belépési pont, amikor a workflow a digestet máshol számolja. A HPDFCMSSignPDFStreamWithProvider egy meglévő aláírás-placeholderre ír alá egy PDF-folyamban, és megőrzi a szabványos /ByteRange futószalagot, ami a megfelelő belépési pont, amikor a HotPDF maga fektette le a placeholdert
A futószalag megőrzése többet számít, mint amennyinek hangzik. A /ByteRange konvenció — két tartomány, amelyek átugorják a hexadecimális aláírás-ablakot — az, amit minden validátor először ellenőriz, és egy szolgáltató-alapú út, amely újraírta volna, megtörné a PAdES-megfelelőséget, bármilyen helyes is a kriptográfia. A HotPDF az elrendezést azonosnak tartja a beépített aláíró úttal, így egy PKCS#11-tokenen keresztül aláírt dokumentum ugyanazzal az aláírás-ellenőrző kóddal ellenőrizhető, mint egy PFX-fájlból aláírt. Az algoritmusválasztás felett álló profil-szabályokhoz lásd a PAdES alapterv-aláírások Delphiben átjárását, az ezt a szolgáltatómodellt megelőző ECDSA-specifikus kódolási csapdákhoz pedig az ECDSA CMS-ellenőrzés és P1363-aláírás-formátumok jegyzeteit
Egy olyan migrációs sorrend, amely nem hagyja cserben a dokumentumaidat
A poszt-kvantum felkészültség ütemterv-probléma, nem kapcsoló. Ma szinte egyetlen telepített PDF-megjelenítő sem validál ML-DSA-t, tehát egy csak azzal aláírt dokumentum az olvasó szempontjából egy olyan dokumentum, amelynek aláírása nem ellenőrizhető. Az a sorrend, amely túléli a valódi archívumokkal való találkozást: tartsuk meg az RSA vagy ECDSA aláírást, amelyet egy validátor ítélni fog, adjuk hozzá a kiterjesztés-deklarációt és egy második ML-DSA-aláírást, ahol egy házirend kvantum-ellenálló bizonyítékot követel, és csak akkor mozgassuk át az elsődleges aláírást, amikor a fogyasztó rendszerek felzárkóztak
Amit a HotPDF ma ad, az a képesség, hogy mindkettőt megírjuk és ellenőrizzük, ugyanabból a kódból, az algoritmus őszintén rögzítve a fájlban és az ellenőrzés eredményében. A HotPDF egy natív VCL PDF komponens Delphihez és C++Builderhez külső PDF futtatókörnyezet nélkül, így az aláíró és ellenőrző utak a saját futtatható állományunkon belül élnek, nem mellette — a teljes funkciólistát és a próbaverziót a HotPDF Delphi PDF komponens oldal tartalmazza