Allekirjoituksen jälkeen muuttunut allekirjoitettu PDF ei ole automaattisesti rikki. ISO 32000-1 sallii asteittaiset päivitykset allekirjoituksen päälle, ja vain jotkin niistä rikkovat allekirjoittajan asettaman käytännön. HotPDF Component Delphille ja C++Builderille vastaa tähän kysymykseen AnalyzeLoadedSignatureRevisions-funktiolla, joka luokittelee jokaisen allekirjoituksen jälkeisen revision ja arvioi sen DocMDP:tä ja FieldMDP:tä vasten. Skenaario on tuttu kaikille sopimusohjelmistoja toimittaville: asiakkaasi allekirjoittaa ostosopimuksen, lähettää sen ulos ja saa sen takaisin liitesivu kiinnitettynä. Lukija näyttää keltaisen palkin, joka sanoo allekirjoituksen olevan ehjä mutta asiakirjaa on muutettu allekirjoituksen jälkeen, eikä kukaan huoneessa osaa sanoa, onko kyse tavanomaisesta vastaallekirjoitustyönkulusta vai jostakusta, joka hiljaa muokkaa allekirjoitettua sopimusta
Mikä lasketaan lailliseksi muutokseksi allekirjoituksen jälkeen?
Muutos on laillinen, kun sen semanttinen kategoria osuu varmentavan allekirjoituksen ilmoittaman käyttöoikeuden sisään. ISO 32000-1 §12.8.2.2 määrittelee DocMDP-muunnoksen /P-arvolla 1, 2 tai 3: 1 ei salli mitään muutoksia, 2 sallii lomakkeen täytön ja allekirjoittamisen, 3 sallii lomakkeen täytön, allekirjoittamisen ja merkinnät. HotPDF paljastaa nämä THPDFDocMDPPermission-arvoina dmpNoChanges, dmpFormFillAndSign ja dmpFormFillSignAndAnnotate, ja dmpNone on varattu tarkastustuloksille, jotka eivät kanna lainkaan DocMDP-muunnosta
Kategoriat ovat järjestyksessä, ja tuo järjestys on koko tarkastuksen moottori. THPDFRevisionModificationLevel ajaa rmlNone-, rmlLongTermValidation-, rmlFormFillAndSign-, rmlAnnotations- ja rmlOther-arvot, tarkoituksella järjestettynä niin, että suurempi järjestysluku ei koskaan ole vähemmän rajoittava. Koko asiakirja pelkistyy suurimpaan tasoon, joka havaitaan kaikissa allekirjoituksen jälkeisissä revisioissa, ja DocMDP-vertailusta tulee yksinkertainen kokonaislukutesti. Yksi vivahde on tärkeä varhain: kohdassa dmpNoChanges analyysi hyväksyy silti rmlLongTermValidation-tason. DSS- ja VRI-vahvistusmateriaalin tai asiakirjan aikaleiman lisääminen varmennettuun tiedostoon on allekirjoituksen ylläpitoa, ei asiakirjan muokkausta, ja sen käsitteleminen rikkomuksena rikkoisi jokaisen olemassa olevan pitkäaikaisarkistointityönkulun
Miten HotPDF rakentaa revisioketjun uudelleen?
Rakenteellisesti, ei heuristisesti. ISO 32000-1 §7.5.6:n mukaan asteittainen päivitys liittää uuden ristiviittausosion, jonka /Prev osoittaa edelliseen, joten HotPDF lukee startxref:n hännästä, jäsentää siellä olevan osion, seuraa /Prev:iä taaksepäin ja toistaa, palauttaen osiot vanhin ensin. Kaksi turvarajaa istuu tuossa silmukassa, ja molemmat kannattaa tuntea, kun triagoi tiedostoa, joka epäonnistuu: /Prev, joka osoittaa jo käydyn siirtymän kohtaan, päättää kävelyn nimenomaisella syklidiagnoosilla sen sijaan, että pyörisi loputtomiin, ja tuhatta revisiota pidempi ketju hylätään suoralta kädeltä. Molemmat nousevat esiin Analysis.Issue:ssa funktion palauttaessa False, eikä kumpaakaan pitäisi peittää, koska syklinen /Prev on väärin muodostettu tai vihamielinen tiedosto eikä pelkästään epätavallinen
Neljä historiallista muotoa ilmenee todellisissa asiakirjoissa, ja kaikkia neljää käsitellään: perinteiset xref-taulukot jäsennetään rivi riviltä, ristiviittausvirrat puretaan ja dekoodataan niiden /W- ja /Index-kenttien kautta, hybridiviittaustiedostot, joiden perinteinen loppuosa kantaa /XRefStm-avaimen, joka jäsennetään ja yhdistetään samaan revisioon (Office-tuottajan tapaus, käsitelty artikkelissa hybridiristiviittausvirrat), ja objektit, jotka asuvat ObjStm-säilön sisällä, mikä on merkityksellistä, koska moderni päivitys yleensä sijoittaa muuttuneen sanakirjan pakattuun virtaan sen sijaan, että kirjoittaisi sen suoraan, kuten kuvataan artikkelissa objektivirrat ja asteittaiset päivitykset. Allekirjoitus ankkuroi jaon: /ByteRange[2] + /ByteRange[3]:sta tulee SignedRevisionLength, ja jokainen osio tuossa siirtymässä tai sen jälkeen on allekirjoituksen jälkeinen. Se, tiivistyykö tavualue edelleen oikein, on erillinen kysymys, johon vastaa VerifyLoadedSignature ja joka käsitellään artikkelissa PDF-digitaalisten allekirjoitusten varmentaminen
Miten jokainen muuttunut objekti luokitellaan
Luokittelu ajetaan objektikohtaisesti, ja sitten se etenee viittausten mukana. Jokaiselle objektinumerolle, jota allekirjoituksen jälkeinen osio koskettaa, HotPDF lukee uuden rungon ja rungon sellaisena kuin se oli allekirjoitetussa tilannekuvassa; identtinen runko on rmlNone, koska tuottajat todella kirjoittavat objekteja uudelleen muuttamatta niitä. Tunnistimet ovat tarkoituksella kapeat. Objekti, jonka /Type /DocTimeStamp, tai jonka /SubFilter on ETSI.RFC3161, on rmlLongTermValidation, samoin kuin mikä tahansa luettelon /DSS-puusta saavutettavissa oleva; /Type /Sig-sanakirja on rmlFormFillAndSign. Säilöille testi on, mitkä avaimet liikkuivat, ei se, mikä objekti on: luettelo saa vain kasvaa tai muuttaa /DSS-, /Extensions- tai /AcroForm-arvoaan; AcroForm-sanakirja vain /Fields-, /SigFlags-, /NeedAppearances-, /DR-, /DA- tai /Q-arvoaan; sivu vain /Annots-arvoaan; kenttä tai widget vain /V-, /AP-, /AS- tai /M-arvoaan. Kaikki näiden joukkojen ulkopuolella olevat putoavat tasolle rmlOther, mikä on täsmälleen se tapa, jolla liitetty liitesivu jää kiinni: sivun lisääminen järjestää sivupuun uudelleen tavoilla, joita mikään sallittujen lista ei kata, eikä mikään määrä laillista lomakkeen täyttöä muistuta sitä
Sitten tasot etenevät, ja jokainen säilö perii osoittamiensa muuttuneiden lasten suurimman tason, iteroiden kunnes osoitus vakautuu. Tämä on se, mikä saa ulkoasuvirrat toimimaan. Täytetty tekstikenttä kirjoittaa /V:n uudelleen ja osoittaa tuoreeseen /AP-virtaan, ja tuo virta yksinään on nimetön joukko sisältöoperaattoreita ilman tyyppiä, jota tunnistaa; koska kenttä, joka omistaa sen, on rmlFormFillAndSign, virta perii saman tason sen sijaan, että putoaisi luokkaan rmlOther. Sama periytyminen kantaa DSS-kontekstin sertifikaatti- ja peruutusvirroille, jotka muuten olisivat luokittelemattomia
Miksi lukukelvoton objekti lasketaan rikkomukseksi?
Koska vaihtoehto olisi validaattori, jonka voittaa kirjoittamalla jotain, mitä se ei ymmärrä. Kolme tilannetta päätyy HotPDF:ssä tasolle rmlOther ilman valitusmahdollisuutta: objekti, jonka runkoa ei voitu lukea revisiosta, objekti, jonka revisio merkitsee vapautetuksi, ja objekti, joka ei täsmää mihinkään yllä olevista tunnistimista. Jokainen tallentaa erityisen diagnoosin revision Issue-kenttään, joten operaattori näkee, mikä objektinumero tuotti tuomion
Vapauttaminen on kolmesta terävin. Allekirjoituksen jälkeinen revisio, joka merkitsee aiemmin määritellyn objektin vapaaksi, on poistanut sisältöä allekirjoitetusta asiakirjasta, eikä mikään §12.8.2.2:n mukainen käyttöoikeustaso salli sitä; objektinumerot päätyvät FreedObjectNumbers-kenttään ja revisio nostetaan tasolle rmlOther. Lukukelvottomat objektit noudattavat samaa logiikkaa eri syystä. Validaattorilla, joka ei voi jäsentää objektia, ei ole perustetta kutsua sitä vaarattomaksi, ja rehellinen vastaus siihen ei ole hiljaisuus. Epätavallisen mutta harmittoman rakenteen raportoiminen rikkomuksena maksaa ihmisen tarkastuksen; vastakkainen virhe toimittaa allekirjoitetun sopimuksen, jossa on huomaamaton muokkaus sisällä
Tuomion lukeminen Delphissä
Kutsu on lyhyt. Lataa asiakirja, valitse allekirjoituksen indeksi, lue tietue; parametriton ylikuormitusversio avaa uudelleen tiedoston, josta asiakirja ladattiin, ja TStream-ylikuormitusversio ottaa kutsujan antamat tavut ja palauttaa virran position ennen paluuta. PolicyCompliant on se yksi totuusarvo, jonka useimmat kutsujat haluavat, yhdistäen kolme itsenäistä päätöstä: käyttöoikeussanakirjojen rakenteellisen kelvollisuuden, DocMDPCompliant:n ja FieldMDPCompliant:n. Pidä osat näkyvissä käyttöliittymässäsi sen sijaan, että tiivistäisit ne, ja huomaa, että asiakirja, jossa ei ole DocMDP-muunnosta, jättää DocMDPCompliant:n arvoon True, koska tavallinen hyväksymisallekirjoitus ei ilmoita mitään käytäntöä, jota rikkoa, ja koottu ModificationLevel on silloin kuvaileva eikä tuomio
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;
Triagea varten yleensä haluat revisiokohtaisen erittelyn yhteenvedon sijaan, koska se kertoo, milloin asiakirjan historiassa asiat menivät pieleen. Jokainen merkintä Analysis.Revisions:issa kantaa oman indeksinsä ketjussa, ristiviittaussiirtymän, johon se kirjoitettiin, oman muutostasonsa ja mukana olevat objektinumerot
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 arvioidaan erikseen, ja se on tarkoituksellista
Asiakirja voi täyttää DocMDP:n vaatimukset ja silti olla laiton, minkä vuoksi FieldMDPCompliant on erillinen totuusarvo eikä tasovertailuun taitettu. ISO 32000-1 §12.8.2.4 määrittelee FieldMDP-muunnoksen, ja §12.7.5.5 siihen liittyvän /SigFieldLock-merkinnän, jäädyttämään nimetyt lomakekentät allekirjoitushetkellä, vaikka asiakirja kokonaisuutena vielä sallisi lomakkeen täytön. Kentän täyttäminen on tason 2 toimi; allekirjoittajan lukitseman kentän täyttäminen on rikkomus riippumatta tasosta. HotPDF lukee laajuuden THPDFFieldLockAction-tyyppiin arvoina flaAll, flaInclude tai flaExclude, ja flaNone on tuloksille, joissa ei ole lukituskäytäntöä, ja nimet Permissions.FieldNames:iin: flaAll lukitsee kaiken, flaInclude lukitsee luetellut nimet, flaExclude lukitsee kaiken paitsi ne. Yksi yksityiskohta on tärkeä tuloksia luettaessa, sillä vain kentät, jotka olivat jo läsnä allekirjoitetussa tilannekuvassa, raportoidaan ChangedFieldNames:issa, koska kentällä, joka luotiin kokonaan allekirjoituksen jälkeen, ei ole allekirjoitettua tilaa vastaan ristiriitaan, ja se jää sen sijaan DocMDP-polun kiinni
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;
Mitä tämä analyysi ei kerro sinulle
Se ei varmenna allekirjoitusta. AnalyzeLoadedSignatureRevisions päättelee rakenteesta ja käyttöoikeuksista; sen, tiivistyykö allekirjoitettu tavualue edelleen CMS-blobin arvoon, ja luottaako allekirjoittajan sertifikaatti mihinkään sellaiseen, johon luotat, vastaa VerifyLoadedSignature ja VerifyLoadedSignatureWithTrust. Tiedosto voi olla täysin käytännönmukainen ja kryptografisesti arvoton, joten kaksi tarkistusta kuuluvat rinnakkain jokaiseen todelliseen hyväksymisportiin. Se ei myöskään lue tarkoitusta sisältövirtojen sisältä: sivu, jonka sisältövirta korvattiin kokonaan, jää kiinni muutoksena sallitun listan ulkopuolella, mutta analyysi ei kerro, että korvaus vaihtoi maksuluvun. rmlOther-tuomio tarkoittaa, että ihmisen pitäisi katsoa, ei sitä, että petos tapahtui, ja käytäntöä noudattava tuomio tarkoittaa, että muutos sopii sallittuun kategoriaan, ei sitä, että muutos oli haluttu. Kun tarvitset vain sen, mitä allekirjoittaja ilmoitti, ilman revisiokävelyä, GetLoadedSignaturePermissions palauttaa käytäntösanakirjat yksinään
Kaikki tässä kuvattu ajetaan natiivisti Delphissä ja C++Builderissa ilman ulkoista allekirjoituspalvelua silmukassa, mikä tekee siitä käytännöllistä ajaa jokaisella saapuvalla asiakirjalla eikä vain niillä, joita joku jo epäili. Täysi allekirjoitus- ja revisio-API, mukaan lukien käyttöoikeus- ja varmennusmenetelmät, joiden kanssa se pariutuu, on osa HotPDF Componenttia Delphille ja C++Builderille