PDFiumPas padalina PAdES pasirašymą į du iškvietimus, kad privatus raktas niekada neturėtų būti jūsų procese. PreparePadesRemoteSignature parašo prieauginį atnaujinimą su tuščia fiksuoto pločio /Contents vieta ir grąžina užklausos įrašą, nešantį SHA-256 dokumento santrauką, tikslų ByteRange ir paruošto failo kontrolinį atspaudą. CompletePadesRemoteSignature paima atskirtą CMS, kurį grąžina jūsų pasirašymo tarnyba, ir įdeda jį į tą rezervuotą vietą
Tarp šių dviejų iškvietimų gali praeiti minutės ar valandos, procesas gali persileisti iš naujo, o darbas gali persikelti į kitą mašiną. Būtent dėl šios spragos API taip ir suformuotas
Kodėl nuotolinis raktas negali naudoti įprasto pasirašymo iškvietimo?
Todėl, kad SignPadesBytes daro prielaidą, jog pasirašymo operacija vyksta iškvietimo viduje. Ji sukuria prieauginį atnaujinimą, apskaičiuoja santrauką per ByteRange, pasirašo ją ir įrašo rezultatą – visa tai prieš grįžtant. Tai lygiai teisinga, kai raktas gyvena Windows sertifikatų saugykloje arba PKCS#12 faile, kurį įkėlėte
Tai neįmanoma, kai raktas gyvena tinklo HSM, kvalifikuotame parašo kūrimo įrenginyje, kurį valdo patikimų paslaugų teikėjas, arba debesies pasirašymo API, kuriai reikia, kad naudotojas patvirtintų telefone. Tokiais atvejais seka nėra funkcijos iškvietimas, tai pokalbis: siunčiate santrauką, kažkas kitas autentifikuoja žmogų, ir CMS grįžta vėliau. Sinchroninis API negali išreikšti „vėliau" neužblokuodamas gijos operacijai, kuriai gali prireikti antro faktoriaus
Dviejų fazių protokolas
Pirma fazė paruošia dokumentą. PDFiumPas prideda parašo lauką ir reikšmės žodyną, rezervuoja ContentsSize baitų šešioliktainiu koduotos erdvės /Contents, apskaičiuoja ByteRange aplink šią rezervaciją ir sukuria TPadesRemoteSigningRequest, turintį FormatVersion, PreparedFingerprint, DocumentDigest, keturių elementų ByteRange, ContentsHexOffset ir ContentsSize
Vienintelė reikšmė, kurios reikia jūsų pasirašymo tarnybai, yra DocumentDigest: SHA-256, kurią grąžinta CAdES SignedData turi nešti kaip savo pranešimo santrauką. Visa kita įraše egzistuoja tam, kad antra fazė galėtų įrodyti, jog užbaigiamas failas yra tas failas, iš kurio ta santrauka buvo apskaičiuota
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; // šešioliktainiai baitai, rezervuoti 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;
// Išsaugokite sesiją, kad vėlesnis paleidimas - ar kita mašina - galėtų ją užbaigti
Session := TFileStream.Create('contract.signreq', fmCreate);
try
SavePadesRemoteSigningRequest(Session, Request);
finally
Session.Free;
end;
SendDigestToSigningService(Request.DocumentDigest);
end;
Ką atmeta Complete, ir kodėl kiekviena patikra egzistuoja?
Užbaigimas yra vieta, kur nuotolinio pasirašymo dizainas paprastai suklysta, todėl validacija sąmoningai negailestinga. CompletePadesRemoteSignature atmeta paruoštą PDF, kurio kontrolinis atspaudas nebesutampa su užklausa, ByteRange, nesutampantį su užrašytomis vietos rezervacijos koordinatėmis, pakeistus /Contents skirtukus, vietą, kuri jau nebe tuščia, CMS, didesnį nei rezervacija, CMS, kuris nėra lygiai viena DER reikšmė, nepalaikomą SignedData formą, trūkstamą signing-certificate-v2 atributą, ir CMS, kurio pranešimo santrauka nelygi paruošto dokumento santraukai
Kiekvienas iš jų atitinka realų gedimą. Kontrolinio atspaudo ir ByteRange patikros sugauna atvejį, kai kažkas iš naujo sugeneravo paruoštą failą tarp fazių, o tai sukurtų parašą, kuris patvirtintų baitus, kurių niekas neturi. Tuščios vietos patikra sugauna dvigubą užbaigimą, kai antras CMS įrašomas ant parašo, kuris jau egzistuoja. Pranešimo santraukos patikra sugauna pačią pavojingiausią situaciją: teisingai suformuotą CMS, pasirašytą per kitą dokumentą, o tai gaunama, kai eilė sumaišo dvi vienalaikes pasirašymo sesijas. Be jos gautumėte failą, kuris atrodo pasirašytas ir nepavyksta visur validuojant, arba dar blogiau, kuris neša kažkieno kito patvirtinimą
Signing-certificate-v2 reikalavimas yra PAdES atitikties, ne vientisumo, klausimas. ETSI EN 319 142 reikalauja, kad pasirašymo sertifikatas būtų susietas su pasirašytais atributais, o CMS be šio atributo nėra PAdES parašas, net jei jis kriptografiškai patvirtinamas. Atmesdami jį užbaigimo metu, jūs tai sužinote čia, ne validatoriaus ataskaitoje iš kliento – tema, toliau nagrinėjama straipsnyje apie kodėl validatoriai atmeta PAdES parašus
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; // grąžina HSM arba TSP
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
// Kiekvienas atmetimas turi konkrečią priežastį; registruokite ją pažodžiui
FailSession(E.Message);
end;
finally
Dest.Free;
Prepared.Free;
end;
end;
Peržengiant proceso ir mašinos ribas
SavePadesRemoteSigningRequest ir LoadPadesRemoteSigningRequest serializuoja sesiją per stabilų versijuotą dvejetainį formatą, o būtent tai daro šį dizainą praktišką, ne tik teisingą. Žiniatinklio aplikacija gali paruošti dokumentą vienoje užklausoje, saugoti paruoštą PDF ir sesijos duomenų bloką, grąžinti santrauką naršyklei pasirašymui su lustine kortele ir užbaigti failą visiškai kitame užklausos apdorojiklyje
FormatVersion laukas yra tai, kas tai išlaiko saugu per atnaujinimus. Sesija, parašyta senesnės versijos ir įkelta naujesnės, atpažįstama arba aiškiai atmetama, o ne neteisingai perskaitoma kaip kitaip suformuotas įrašas. Jei jūsų eilė gali laikyti sesijas dienų dienas, traktuokite formato versiją kaip eksploatacinį faktą, vertą registruoti, ne kaip įgyvendinimo detalę
Vietos dydžio nustatymas
ContentsSize yra vienintelis parametras, apie kurį turite pagalvoti, nes jis fiksuojamas prieš CMS atsirandant. Jis skaičiuoja šešioliktainiu koduotą rezervaciją, todėl 6 KB DER CMS reikalauja bent 12 KB erdvės, o realizacija riboja rezervaciją iki 64 MiB
Rezervavus per mažai, užbaigimas nepavyksta su per didelio CMS klaida jau po to, kai jūsų pasirašymo tarnyba atliko savo darbą, o tai matuojamoje kvalifikuoto parašo paslaugoje reiškia švaistytą operaciją. Rezervavus per daug, kiekvienas pasirašytas dokumentas amžinai nešios užpildą. Protingas požiūris yra išmatuoti: pasirašykite vieną dokumentą su tikru sertifikatų grandine, pažiūrėkite DER ilgį, padvigubinkite jį šešioliktainiam, tada pridėkite dosnią atsargą laiko žymos žetonui, jei ketinate atnaujinti iki T lygio parašo. Grandinės su keliais tarpiniais sertifikatais ir ilgu OCSP atsakymu auga greičiau, nei žmonės tikisi
Kas nutinka po parašo
Užbaigtas nuotolinis parašas yra PAdES B-B. Ilgalaikiam patvirtinimui reikia laiko žymos ir patvirtinimo medžiagos, o tai yra atskiras prieauginis atnaujinimas, pridedantis DSS ir jo per parašą VRI žodynus, aprašytas straipsnyje apie ilgalaikius parašus su RFC 3161 laiko žymomis ir DSS. Šis žingsnis vietinis: jis prideda sertifikatus, OCSP atsakymus ir CRL sąrašus, kuriems nė vienam nereikia privataus rakto
Prieš pristatydami, patikrinkite, ką sukūrėte, tuo pačiu kodo keliu, kurį naudotų pasitikinti šalis, aprašytu straipsnyje apie skaitmeninių parašų ir PAdES lygių tikrinimą. Pasirašymas ir patvirtinimas yra skirtingas kodas, ir nuotolinio pasirašymo konvejeris yra tiksliai ta vieta, kur šie du gali išsiskirti, kol išorinis validatorius apie tai nepraneša
PDFiumPas yra Delphi ir Lazarus komponentas aplink PDFium variklį su natyviu Pascal PAdES steku, todėl pasirašymas, laiko žymėjimas ir validacija veikia be išorinių komandinės eilutės įrankių. Pilna API dokumentacija ir bandomoji versija pateikta PDFium Delphi komponento puslapyje