Műszaki cikk

PDFium VCL távoli PAdES aláírás: HSM és felhőkulcsok

A PDFiumPas két hívásra osztja a PAdES aláírást, így a privát kulcsnak soha nem kell a folyamatodban lennie. A PreparePadesRemoteSignature ír egy inkrementális frissítést egy üres, rögzített szélességű /Contents helykitöltővel, és visszaad egy kérésrekordot, amely hordozza a SHA-256 dokumentumdigesztet, a pontos ByteRange-et és az előkészített fájl egy ujjlenyomatát. A CompletePadesRemoteSignature átveszi az aláírási szolgáltatásod által visszaadott leválasztott CMS-t, és beilleszti azt abba a fenntartott helyre

E két hívás között percek vagy órák telhetnek el, a folyamat újraindulhat, és a munka átkerülhet egy másik gépre. Ez a rés az egész oka annak, hogy az API így van kialakítva

Miért nem tud egy távoli kulcs a szokásos aláírási hívást használni?

Mert a SignPadesBytes feltételezi, hogy az aláírási művelet a hívásban belül történik. Felépíti az inkrementális frissítést, kiszámítja a digesztet a ByteRange felett, aláírja azt, és megírja az eredményt, mindezt a visszatérés előtt. Ez pontosan helyes, amikor a kulcs a Windows tanúsítványtárolóban vagy egy általad betöltött PKCS#12 fájlban él

Ez lehetetlen, amikor a kulcs egy hálózati HSM-ben, egy bizalmiszolgáltató által üzemeltetett minősített aláírás-létrehozó eszközben, vagy egy olyan felhőalapú aláírási API-ban él, amely megköveteli, hogy a felhasználó telefonon erősítsen meg valamit. Ezekben az esetekben a sorrend nem egy függvényhívás, hanem egy párbeszéd: elküldesz egy digesztet, valami más hitelesít egy embert, és egy CMS később érkezik vissza. Egy szinkron API nem tudja kifejezni a „később”-t anélkül, hogy blokkolna egy szálat egy olyan műveleten, amely második faktort igényelhet

A kétfázisú protokoll

Az első fázis előkészíti a dokumentumot. A PDFiumPas hozzáfűzi az aláírásmezőt és az értékszótárt, lefoglal ContentsSize byte hex-kódolt helyet a /Contents mezőben, kiszámítja a ByteRange-et e foglalás körül, és előállít egy TPadesRemoteSigningRequest rekordot, amely tartalmazza a FormatVersion, a PreparedFingerprint, a DocumentDigest, a négyelemű ByteRange, a ContentsHexOffset és a ContentsSize értékeket

Az egyetlen érték, amelyre az aláírási szolgáltatásodnak szüksége van, a DocumentDigest: az a SHA-256, amelyet a visszaadott CAdES SignedData-nak üzenetdigesztként hordoznia kell. A rekord minden más eleme azért létezik, hogy a második fázis bizonyítani tudja, hogy az a fájl, amelyet befejez, ugyanaz a fájl, amelyből a digesztet számították

uses
  FPdfPades;

var
  Options: TPadesRemoteSignOptions;
  Request: TPadesRemoteSigningRequest;
  Source, Prepared, Session: TFileStream;
begin
  Options := TPadesRemoteSignOptions.Default;
  Options.Reason := 'Approved by finance';
  Options.Location := 'Lisbon';
  Options.Name := 'A. Moreira';
  Options.SigningTimeUtc := NowUtc;
  Options.ContentsSize := 16384;   // a CMS-nek fenntartott hex byte-ok

  Source := TFileStream.Create('contract.pdf', fmOpenRead or fmShareDenyWrite);
  Prepared := TFileStream.Create('contract.prepared.pdf', fmCreate);
  try
    PreparePadesRemoteSignature(Source, Prepared, Options, Request);
  finally
    Prepared.Free;
    Source.Free;
  end;

  // A munkamenet mentése, hogy egy későbbi futás - vagy egy másik gép - befejezhesse
  Session := TFileStream.Create('contract.signreq', fmCreate);
  try
    SavePadesRemoteSigningRequest(Session, Request);
  finally
    Session.Free;
  end;

  SendDigestToSigningService(Request.DocumentDigest);
end;

Mit utasít el a Complete, és miért létezik minden ellenőrzés?

A befejezés az a pont, ahol egy távoli aláírási tervezés általában elromlik, így a validálás szándékosan könyörtelen. A CompletePadesRemoteSignature elutasít egy előkészített PDF-et, amelynek ujjlenyomata már nem egyezik a kéréssel, egy ByteRange-et, amely nem egyezik a rögzített helykitöltő koordinátáival, módosított /Contents határolókat, egy helykitöltőt, amely már nem üres, egy CMS-t, amely nagyobb a foglalásnál, egy CMS-t, amely nem pontosan egy DER-érték, egy nem támogatott SignedData alakot, egy hiányzó signing-certificate-v2 attribútumot, és egy CMS-t, amelynek üzenetdigesztje nem egyezik az előkészített dokumentumdigeszttel

Ezek mindegyike egy valódi hibára képez le. Az ujjlenyomat- és ByteRange-ellenőrzés elkapja azt az esetet, amikor valaki a fázisok között újragenerálta az előkészített fájlt, ami olyan aláírást eredményezne, amely olyan byte-okkal szemben validál, amelyekkel senki sem rendelkezik. Az üres-helykitöltő ellenőrzés elkapja a dupla befejezést, amikor egy második CMS íródik egy már létező aláírás fölé. Az üzenetdigeszt-ellenőrzés a legveszélyesebb esetet kapja el: egy helyesen formázott CMS-t, amely egy másik dokumentum felett van aláírva, ami akkor fordul elő, amikor egy sor összekever két párhuzamos aláírási munkamenetet. Enélkül olyan fájlt állítanál elő, amely aláírtnak tűnik, és mindenhol elbukik a validáláson, vagy ami rosszabb, valaki más jóváhagyását hordozza

A signing-certificate-v2 követelmény inkább PAdES-megfelelőségi kérdés, mint integritási. Az ETSI EN 319 142 megköveteli, hogy az aláíró tanúsítvány be legyen kötve az aláírt attribútumokba, és egy ilyen attribútumot nélkülöző CMS nem PAdES aláírás, még akkor sem, ha kriptográfiailag ellenőrizhető. Ha ezt a befejezéskor elutasítod, azt jelenti, hogy itt derül ki, nem egy ügyfél validátorjelentésében, ezt a témát a miért utasítják el a validátorok a PAdES aláírásokat című cikk tárgyalja tovább

var
  Request: TPadesRemoteSigningRequest;
  Session, Prepared, Dest: TFileStream;
  CmsDer: TBytes;
begin
  Session := TFileStream.Create('contract.signreq', fmOpenRead);
  try
    Request := LoadPadesRemoteSigningRequest(Session);
  finally
    Session.Free;
  end;

  CmsDer := FetchDetachedCmsFromService;   // az HSM vagy a TSP adja vissza

  Prepared := TFileStream.Create('contract.prepared.pdf', fmOpenRead);
  Dest := TFileStream.Create('contract.signed.pdf', fmCreate);
  try
    try
      CompletePadesRemoteSignature(Prepared, Dest, Request, CmsDer);
    except
      on E: EPadesCrypto do
        // Minden elutasítás egy konkrét okot hordoz; naplózd szó szerint
        FailSession(E.Message);
    end;
  finally
    Dest.Free;
    Prepared.Free;
  end;
end;

Folyamat- és géphatárok átlépése

A SavePadesRemoteSigningRequest és a LoadPadesRemoteSigningRequest egy stabil, verziózott bináris formátumon keresztül szerializálja a munkamenetet, és ez teszi a tervezést gyakorlativá, nem csupán helyessé. Egy webalkalmazás egy kérésben előkészíthet egy dokumentumot, tárolhatja az előkészített PDF-et és a munkamenet-blobot, visszaadhat egy digesztet a böngészőnek egy smart card aláíráshoz, és befejezheti a fájlt egy teljesen másik kérés-kezelőben

A FormatVersion mező tartja ezt biztonságosan a frissítéseken keresztül. Egy régebbi build által írt, és egy újabb build által betöltött munkamenetet explicit módon felismer vagy elutasít, ahelyett hogy egy másképp formázott rekordként félreolvasná. Ha a sorod napokig tud munkameneteket tárolni, kezeld a formátumverziót naplózásra érdemes üzemeltetési tényként, ne implementációs részletként

A helykitöltő méretezése

A ContentsSize az az egyetlen paraméter, amelyen el kell gondolkodnod, mert rögzítve van, mielőtt a CMS létezne. A hex-kódolt foglalást számolja, így egy 6 KB-os DER CMS legalább 12 KB helyet igényel, és az implementáció 64 MiB-ra korlátozza a foglalást

Ha túl keveset foglalsz, a befejezés egy túlméretezett-CMS hibával meghiúsul, miután az aláírási szolgáltatásod már elvégezte a munkáját, ami egy mért, minősített aláírási szolgáltatásnál egy elpazarolt műveletet jelent. Ha túl sokat foglalsz, minden aláírt dokumentum örökre magával hordozza a kitöltést. Az ésszerű megközelítés a mérés: írj alá egy dokumentumot a valódi tanúsítványláncoddal, nézd meg a DER hosszát, dupláld meg hexhez, majd adj hozzá bőséges tartalékot az időbélyeg-tokenhez, ha T-szintű aláírásra tervezel frissíteni. A több közbenső tanúsítvánnyal és hosszú OCSP-válasszal rendelkező láncok gyorsabban nőnek, mint azt az emberek várják

Mi jön az aláírás után

Egy befejezett távoli aláírás PAdES B-B. A hosszú távú validáláshoz egy időbélyeg és a validálási anyag szükséges, ami egy külön inkrementális frissítés, amely hozzáad egy DSS-t és annak aláírásonkénti VRI szótárait, ahogy azt a hosszú távú aláírások RFC 3161 időbélyegekkel és DSS-sel című cikk leírja. Ez a lépés lokális: tanúsítványokat, OCSP-válaszokat és CRL-eket ad hozzá, amelyek közül egyik sem igényli a privát kulcsot

Kiszállítás előtt ellenőrizd azt, amit előállítottál, ugyanazzal a kódútvonallal, amelyet egy megbízó fél használna, ahogy azt a digitális aláírások és PAdES-szintek vizsgálata című cikk tárgyalja. Az aláírás és az ellenőrzés különböző kód, és egy távoli aláírási pipeline pontosan az a hely, ahol a kettő anélkül tud eltávolodni egymástól, hogy bárki észrevenné, amíg egy külső validátor meg nem mondja

A PDFiumPas egy Delphi és Lazarus komponens a PDFium motor köré építve, natív Pascal PAdES veremmel, így az aláírás, az időbélyegzés és a validálás külső parancssori eszközök nélkül működik. A teljes API-dokumentáció és egy próbaverzió a PDFium Delphi komponens oldalán található