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