Potpisan PDF koji se promenio posle potpisivanja nije automatski pokvaren. ISO 32000-1 dozvoljava inkrementalna ažuriranja preko potpisa, i samo neka od njih krše politiku koju je potpisnik postavio. HotPDF Component za Delphi i C++Builder odgovara na to pitanje pomoću AnalyzeLoadedSignatureRevisions, koji klasifikuje svaku reviziju posle potpisa i ocenjuje je naspram DocMDP i FieldMDP. Scenario je poznat svakome ko isporučuje softver za ugovore: vaš klijent potpiše ugovor o kupovini, pošalje ga, i dobije ga nazad sa prikačenom stranicom aneksa. Čitač pokazuje žutu traku koja kaže da je potpis netaknut, ali da je dokument izmenjen otkad je potpisan, i niko u prostoriji ne može reći da li je to normalan tok kontrapotpisa ili neko ko tiho menja potpisan ugovor
Šta se računa kao legalna izmena posle potpisivanja?
Izmena je legalna kad njena semantička kategorija upada unutar dozvole koju je sertifikujući potpis deklarisao. ISO 32000-1 §12.8.2.2 definiše DocMDP transformaciju sa vrednošću /P 1, 2 ili 3: 1 ne dozvoljava nikakve izmene, 2 dozvoljava popunjavanje formulara i potpisivanje, 3 dozvoljava popunjavanje formulara, potpisivanje i anotacije. HotPDF izlaže ih kao vrednosti THPDFDocMDPPermission dmpNoChanges, dmpFormFillAndSign i dmpFormFillSignAndAnnotate, sa dmpNone rezervisanim za rezultate inspekcije koji ne nose nikakvu DocMDP transformaciju
Kategorije su uređene, i taj redosled je motor cele provere. THPDFRevisionModificationLevel ide rmlNone, rmlLongTermValidation, rmlFormFillAndSign, rmlAnnotations, rmlOther, namerno raspoređeni tako da veći ordinal nikad nije manje restriktivan. Ceo dokument se svodi na maksimalan nivo primećen kroz svaku reviziju posle potpisa, i DocMDP poređenje postaje jedan test celog broja. Jedna nijansa je bitna rano: na dmpNoChanges analiza i dalje prihvata rmlLongTermValidation. Dodavanje DSS i VRI materijala validacije ili vremenskog žiga dokumenta u sertifikovan fajl je održavanje potpisa, ne izmena dokumenta, i tretiranje toga kao prekršaja bi razbilo svaki postojeći tok dugoročnog arhiviranja
Kako HotPDF ponovo gradi lanac revizija?
Strukturno, ne heuristički. Po ISO 32000-1 §7.5.6, inkrementalno ažuriranje nadovezuje novi cross-reference odeljak čiji /Prev pokazuje na prethodni, tako da HotPDF čita startxref sa kraja, parsira odeljak tamo, prati /Prev unazad i ponavlja, vraćajući odeljke od najstarijeg do najnovijeg. Dva bezbednosna ograničenja sede u toj petlji i oba je vredno znati kad se trijažira fajl koji otkazuje: /Prev koji pokazuje na već posećen offset prekida obilazak sa eksplicitnom dijagnostikom ciklusa umesto da se vrti u krug, a lanac duži od hiljadu revizija se u potpunosti odbija. Oba se pojavljuju u Analysis.Issue sa funkcijom koja vraća False, i nijedno ne treba prekrivati, jer je ciklični /Prev deformisan ili neprijateljski fajl, a ne samo neobičan
Četiri istorijska oblika se pojavljuju u realnim dokumentima i sva četiri su obrađena: tradicionalne xref tabele parsirane red po red, cross-reference tokovi dekomprimovani i dekodovani kroz svoja polja /W i /Index, hibridni fajlovi čiji tradicionalni trailer nosi ključ /XRefStm koji se parsira i spaja u istu reviziju (slučaj Office producenta, pokriven u članku o hibridnim cross-reference tokovima), i objekti koji žive unutar kontejnera ObjStm, koji su bitni jer moderno ažuriranje obično stavlja izmenjen rečnik u komprimovan tok umesto da ga piše direktno, kako je opisano u tekstu o object streams i inkrementalnim ažuriranjima. Potpis usidruje podelu: /ByteRange[2] + /ByteRange[3] postaje SignedRevisionLength, i svaki odeljak na tom offsetu ili posle njega je posle-potpisa. Da li se opseg bajtova i dalje ispravno heš-uje je odvojeno pitanje, na koje odgovara VerifyLoadedSignature, pokriveno u članku o verifikaciji digitalnih PDF potpisa
Kako se svaki izmenjeni objekat klasifikuje
Klasifikacija se izvodi po objektu, a zatim se propagira duž referenci. Za svaki broj objekta koji odeljak posle potpisa dodirne, HotPDF čita novo telo i telo kakvo je stajalo u potpisanom snimku; identično telo je rmlNone, jer producenti zaista ponovo pišu objekte bez izmene. Prepoznavaoci su namerno uski. Objekat sa /Type /DocTimeStamp, ili onaj čiji je /SubFilter ETSI.RFC3161, je rmlLongTermValidation, kao i sve dostupno iz stabla /DSS kataloga; rečnik /Type /Sig je rmlFormFillAndSign. Za kontejnere test je koji ključevi su se pomerili, ne šta je objekat: katalog sme samo da dobije ili izmeni /DSS, /Extensions ili /AcroForm; AcroForm rečnik samo /Fields, /SigFlags, /NeedAppearances, /DR, /DA ili /Q; stranica samo /Annots; polje ili widget samo /V, /AP, /AS ili /M. Sve van tih skupova pada na rmlOther, što je tačno kako se prikačena stranica aneksa hvata: dodavanje stranice preuređuje stablo stranica na način koji nijedna bela lista ne pokriva, i nijedna količina legitimnog popunjavanja formulara tome ne liči
Zatim se nivoi propagiraju, pri čemu svaki kontejner nasleđuje maksimalan nivo izmenjene dece na koju pokazuje, iterirano dok dodela ne stabilizuje. To je ono što čini da tokovi izgleda rade. Popunjeno tekstualno polje ponovo piše /V i pokazuje na svež tok /AP, a taj tok je sam po sebi anonimna gomila operatora sadržaja bez tipa za prepoznavanje; pošto je polje koje ga poseduje rmlFormFillAndSign, tok nasleđuje isti nivo umesto da padne na rmlOther. Ista propagacija nosi DSS kontekst na tokove sertifikata i opoziva koji bi inače bili neklasifikovani
Zašto se nečitljiv objekat računa kao prekršaj?
Zato što je alternativa validator poražen pisanjem nečega što ne razume. Tri situacije se završavaju na rmlOther bez žalbe u HotPDF: objekat čije telo se nije moglo pročitati iz revizije, objekat koji revizija označava kao oslobođen, i objekat koji ne odgovara nijednom od gore navedenih prepoznavalaca. Svaki beleži specifičnu dijagnostiku u polju Issue revizije, tako da operater može videti koji je broj objekta proizveo verdikt
Oslobađanje je najoštrije od tri. Revizija posle potpisa koja označava prethodno definisan objekat kao slobodan je obrisala sadržaj iz potpisanog dokumenta, i nijedan nivo dozvole pod §12.8.2.2 to ne dozvoljava; brojevi objekata slete u FreedObjectNumbers i revizija se podiže na rmlOther. Nečitljivi objekti prate istu logiku iz drugačijeg razloga. Validator koji ne može da parsira objekat nema osnov da ga proglasi bezopasnim, a iskren odgovor na to nije ćutanje. Prijavljivanje neobične ali bezopasne konstrukcije kao prekršaja košta ljudsku reviziju; suprotna greška isporučuje potpisan ugovor sa neprimećenom izmenom unutra
Čitanje verdikta u Delphi
Poziv je kratak. Učitajte dokument, izaberite indeks potpisa, pročitajte zapis; preopterećenje bez parametara ponovo otvara fajl iz kog je dokument učitan, a preopterećenje TStream uzima bajtove koje pozivalac obezbedi i vraća poziciju toka pre povratka. PolicyCompliant je jedini boolean koji većina pozivalaca želi, kombinujući tri nezavisne odluke: strukturalnu validnost rečnika dozvola, DocMDPCompliant, i FieldMDPCompliant. Držite komponente vidljivim u svom UI umesto da ih sažimate, i primetite da dokument bez DocMDP transformacije ostavlja DocMDPCompliant na True, pošto obično odobravajući potpis ne deklariše nikakvu politiku za kršenje, a agregat ModificationLevel je onda deskriptivan, a ne verdikt
var
Pdf: THotPDF;
Analysis: THPDFSignatureRevisionAnalysis;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('contract-countersigned.pdf') > 0 then
begin
if Pdf.AnalyzeLoadedSignatureRevisions(0, Analysis) then
begin
if Analysis.PolicyCompliant then
Writeln('Post-signature changes stay inside the signing policy')
else
Writeln('Policy violation: ', string(Analysis.Issue));
end
else
Writeln('Analysis could not run: ', string(Analysis.Issue));
end;
finally
Pdf.Free;
end;
end;
Za trijažu obično želite razlaganje po reviziji umesto sažetka, jer ono kaže kada su u istoriji dokumenta stvari pošle po zlu. Svaki unos u Analysis.Revisions nosi svoj indeks u lancu, cross-reference offset na kom je napisan, sopstveni nivo izmene, i uključene brojeve objekata
const
LevelNames: array[THPDFRevisionModificationLevel] of string =
('none', 'long-term validation', 'form fill and sign',
'annotations', 'other');
var
I: Integer;
begin
Writeln(Format('%d revisions in chain, signature sits at index %d',
[Analysis.TotalRevisionCount, Analysis.SignedRevisionIndex]));
for I := 0 to High(Analysis.Revisions) do
Writeln(Format(' rev %d at offset %d: %s (%d changed, %d freed) %s',
[Analysis.Revisions[I].RevisionIndex,
Analysis.Revisions[I].XRefOffset,
LevelNames[Analysis.Revisions[I].ModificationLevel],
Length(Analysis.Revisions[I].ChangedObjectNumbers),
Length(Analysis.Revisions[I].FreedObjectNumbers),
string(Analysis.Revisions[I].Issue)]));
end;
FieldMDP se ocenjuje odvojeno, i to je namerno
Dokument može zadovoljiti DocMDP i i dalje biti nelegitiman, zbog čega je FieldMDPCompliant zaseban boolean, a ne uklopljen u poređenje nivoa. ISO 32000-1 §12.8.2.4 definiše FieldMDP transformaciju, a §12.7.5.5 povezani unos /SigFieldLock, da zamrznu imenovana polja formulara u trenutku potpisivanja, čak i tamo gde dokument u celini i dalje dozvoljava popunjavanje formulara. Popunjavanje polja je akcija nivoa 2; popunjavanje polja koje je potpisnik zaključao je prekršaj bez obzira na nivo. HotPDF čita opseg u THPDFFieldLockAction kao flaAll, flaInclude ili flaExclude, sa flaNone za rezultate bez ikakve politike zaključavanja, a imena u Permissions.FieldNames: flaAll zaključava sve, flaInclude zaključava navedena imena, flaExclude zaključava sve osim njih. Jedan detalj je bitan pri čitanju rezultata, u tome što se samo polja već prisutna u potpisanom snimku prijavljuju u ChangedFieldNames, jer polje kreirano u potpunosti posle potpisivanja nema potpisano stanje kome bi protivrečilo i umesto toga ga hvata DocMDP putanja
var
Source: TFileStream;
Analysis: THPDFSignatureRevisionAnalysis;
I: Integer;
begin
Source := TFileStream.Create('contract.pdf', fmOpenRead or fmShareDenyWrite);
try
if Pdf.AnalyzeLoadedSignatureRevisions(0, Source, Analysis) then
if Analysis.Permissions.HasFieldMDP and (not Analysis.FieldMDPCompliant) then
for I := 0 to High(Analysis.ChangedFieldNames) do
Writeln('modified after locking: ',
string(Analysis.ChangedFieldNames[I]));
finally
Source.Free; // stream position was restored before the call returned
end;
end;
Šta ova analiza neće reći
Ne verifikuje potpis. AnalyzeLoadedSignatureRevisions zaključuje o strukturi i dozvolama; da li se potpisan opseg bajtova i dalje heš-uje na vrednost u CMS blob-u, i da li se lanac sertifikata potpisnika oslanja na nešto čemu verujete, odgovaraju VerifyLoadedSignature i VerifyLoadedSignatureWithTrust. Fajl može biti savršeno usklađen sa politikom, a kriptografski bezvredan, tako da dve provere pripadaju jedna uz drugu u bilo kojoj realnoj kapiji prihvatanja. Takođe ne čita nameru unutar tokova sadržaja: stranica čiji je tok sadržaja u potpunosti zamenjen se hvata kao izmena van bele liste, ali analiza vam neće reći da je zamena zamenila iznos plaćanja. Verdikt rmlOther znači da bi čovek trebalo da pogleda, ne da je došlo do prevare, a usaglašen verdikt znači da izmena upada u dozvoljenu kategoriju, ne da je izmena bila željena. Kad vam treba samo ono što je potpisnik deklarisao, bez obilaska revizija, GetLoadedSignaturePermissions vraća rečnike politike same za sebe
Sve opisano ovde radi nativno u Delphi i C++Builder bez ikakvog eksternog servisa za potpisivanje u petlji, što ga čini praktičnim za pokretanje na svakom dolaznom dokumentu umesto samo na onima za koje je neko već posumnjao. Kompletan API potpisa i revizije, uključujući metode dozvola i verifikacije sa kojima se sparuje, deo je HotPDF Component za Delphi i C++Builder