Tehnički članak

Udaljeno PAdES potpisivanje u PDFium VCL: HSM sesije

PDFiumPas dijeli PAdES potpisivanje na dva poziva, tako da privatni ključ nikad ne mora biti u vašem procesu. PreparePadesRemoteSignature zapisuje inkrementalno ažuriranje s praznim placeholderom fiksne širine /Contents i vraća zapis zahtjeva koji nosi SHA-256 sažetak dokumenta, točan ByteRange i otisak pripremljene datoteke. CompletePadesRemoteSignature uzima odvojeni (detached) CMS koji vraća vaša usluga za potpisivanje i umeće ga u to rezervirano mjesto

Između ta dva poziva mogu proći minute ili sati, proces se može ponovno pokrenuti, a posao se može premjestiti na drugi stroj. Upravo je taj razmak cijeli razlog zašto je API oblikovan na ovaj način

Zašto udaljeni ključ ne može koristiti obični poziv za potpisivanje?

Zato što SignPadesBytes pretpostavlja da se operacija potpisivanja odvija unutar samog poziva. Gradi inkrementalno ažuriranje, izračunava sažetak nad ByteRangeom, potpisuje ga i zapisuje rezultat, sve prije nego što vrati kontrolu. To je potpuno ispravno kada ključ živi u Windows spremištu certifikata ili u PKCS#12 datoteci koju ste učitali

Nemoguće je kada ključ živi u mrežnom HSM-u, kvalificiranom uređaju za izradu potpisa kojim upravlja pružatelj usluga povjerenja, ili u cloud API-ju za potpisivanje koji od korisnika zahtijeva potvrdu na mobitelu. U tim slučajevima slijed nije poziv funkcije, već razgovor: pošaljete sažetak, nešto drugo autenticira čovjeka, a CMS stiže kasnije. Sinkroni API ne može izraziti „kasnije” bez blokiranja niti na operaciji kojoj možda treba drugi faktor

Dvofazni protokol

Prva faza priprema dokument. PDFiumPas dodaje polje potpisa i rječnik vrijednosti, rezervira ContentsSize bajtova heksadecimalno kodiranog prostora u /Contents, izračunava ByteRange oko te rezervacije i proizvodi TPadesRemoteSigningRequest koji sadrži FormatVersion, PreparedFingerprint, DocumentDigest, četveroelementni ByteRange, ContentsHexOffset i ContentsSize

Jedina vrijednost koja treba vašoj usluzi za potpisivanje jest DocumentDigest: SHA-256 koji vraćeni CAdES SignedData mora nositi kao svoj sažetak poruke. Sve ostalo u zapisu postoji kako bi druga faza mogla dokazati da je datoteka koju dovršava upravo ona datoteka iz koje je taj sažetak izračunat

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;   // heksadecimalni bajtovi rezervirani za CMS

  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;

  // Pohranite sesiju tako da je kasnije pokretanje - ili drugi stroj - može dovršiti
  Session := TFileStream.Create('contract.signreq', fmCreate);
  try
    SavePadesRemoteSigningRequest(Session, Request);
  finally
    Session.Free;
  end;

  SendDigestToSigningService(Request.DocumentDigest);
end;

Što Complete odbija, i zašto svaka provjera postoji?

Dovršetak je mjesto na kojem dizajn udaljenog potpisivanja obično zakaže, pa je validacija namjerno nemilosrdna. CompletePadesRemoteSignature odbija pripremljeni PDF čiji otisak više ne odgovara zahtjevu, ByteRange koji se ne podudara sa zabilježenim koordinatama placeholdera, izmijenjene graničnike /Contents, placeholder koji više nije prazan, CMS veći od rezervacije, CMS koji nije točno jedna DER vrijednost, nepodržani oblik SignedData, nedostajući atribut signing-certificate-v2 te CMS čiji se sažetak poruke ne podudara s pripremljenim sažetkom dokumenta

Svaka od tih provjera odgovara stvarnom kvaru. Provjere otiska i ByteRangea hvataju slučaj u kojem je netko regenerirao pripremljenu datoteku između faza, što bi proizvelo potpis koji se provjerava nad bajtovima koje nitko nema. Provjera praznog placeholdera hvata dvostruko dovršavanje, kada se drugi CMS zapiše preko potpisa koji već postoji. Provjera sažetka poruke hvata najopasniji slučaj od svih: ispravno oblikovan CMS potpisan nad drugim dokumentom, što dobijete kada red čekanja pomiješa dvije istovremene sesije potpisivanja. Bez nje biste proizveli datoteku koja izgleda potpisano, a svugdje ne uspijeva validacija, ili gore, koja nosi tuđe odobrenje

Zahtjev signing-certificate-v2 pitanje je sukladnosti s PAdES-om, a ne pitanje integriteta. ETSI EN 319 142 zahtijeva da certifikat za potpisivanje bude vezan unutar potpisanih atributa, a CMS kojem taj atribut nedostaje nije PAdES potpis čak i ako se kriptografski uspješno provjerava. Odbacivanje toga pri dovršetku znači da ćete to otkriti ovdje, a ne u izvještaju validatora od kupca, temi koja je dalje istražena u članku zašto validatori odbijaju PAdES potpise

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;   // vraćeno od HSM-a ili TSP-a

  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
        // Svako odbijanje nosi specifičan razlog; zabilježite ga doslovno
        FailSession(E.Message);
    end;
  finally
    Dest.Free;
    Prepared.Free;
  end;
end;

Prelazak granica procesa i stroja

SavePadesRemoteSigningRequest i LoadPadesRemoteSigningRequest serijaliziraju sesiju kroz stabilan verzionirani binarni format, i upravo to čini dizajn praktičnim, a ne samo ispravnim. Web aplikacija može pripremiti dokument u jednom zahtjevu, pohraniti pripremljeni PDF i blob sesije, vratiti sažetak pregledniku radi potpisa pametnom karticom te dovršiti datoteku u posve drugom rukovatelju zahtjeva

Polje FormatVersion ono je koje to čuva sigurnim kroz nadogradnje. Sesija zapisana starijom verzijom, a učitana novijom, prepoznaje se ili eksplicitno odbacuje, umjesto da se pogrešno pročita kao drugačije oblikovan zapis. Ako vaš red čekanja može držati sesije danima, tretirajte verziju formata kao operativnu činjenicu vrijednu bilježenja, a ne kao detalj implementacije

Određivanje veličine placeholdera

ContentsSize je jedini parametar o kojem morate razmišljati, jer je fiksiran prije nego što CMS uopće postoji. Broji heksadecimalno kodiranu rezervaciju, pa DER CMS od 6 KB treba najmanje 12 KB prostora, a implementacija ograničava rezervaciju na 64 MiB

Rezervirate li premalo, dovršetak ne uspijeva s pogreškom o prevelikom CMS-u nakon što je vaša usluga za potpisivanje već obavila posao, što kod naplative usluge kvalificiranog potpisa znači protraćenu operaciju. Rezervirate li previše, svaki potpisani dokument zauvijek nosi popunu. Razuman je pristup mjeriti: potpišite jedan dokument svojim stvarnim lancem certifikata, pogledajte duljinu DER-a, udvostručite je za heksadecimalni zapis, a zatim dodajte velikodušan prostor za token vremenske oznake ako namjeravate nadograditi na potpis T razine. Lanci s više posrednih certifikata i dugim OCSP odgovorom rastu brže nego što ljudi očekuju

Što dolazi nakon potpisa

Dovršen udaljeni potpis je PAdES B-B. Dugoročna validacija treba vremensku oznaku i materijal za validaciju, što je zasebno inkrementalno ažuriranje koje dodaje DSS i njegove VRI rječnike po pojedinom potpisu, opisano u članku dugoročni potpisi s RFC 3161 vremenskim oznakama i DSS-om. Taj je korak lokalan: dodaje certifikate, OCSP odgovore i CRL-ove, od čega ništa ne treba privatni ključ

Prije isporuke, provjerite ono što ste proizveli istom putanjom koda koju bi koristila strana koja se oslanja na potpis, obrađeno u članku inspekcija digitalnih potpisa i razina PAdES-a. Potpisivanje i provjera različit su kod, a pipeline za udaljeno potpisivanje upravo je mjesto na kojem se to dvoje može razići a da nitko ne primijeti, sve dok to ne kaže vanjski validator

PDFiumPas je Delphi i Lazarus komponenta izgrađena oko PDFium engine-a s izvornim PAdES slojem u Pascalu, tako da potpisivanje, vremenske oznake i validacija rade bez vanjskih alata naredbenog retka. Potpuna API dokumentacija i probna verzija dostupne su na stranici PDFium Delphi komponente