HotPDF varmistaa ladattujen PDF-dokumenttien digitaaliset allekirjoitukset kolmella THotPDF-metodilla: GetLoadedSignatureInfo, VerifyLoadedSignature ja VerifyLoadedSignatureEx, jotka esiteltiin versiossa v2.259.0. Komponentti laskee uudelleen alkuperäisen tiedoston /ByteRange-osioiden tiivisteet, tarkistaa CMS-määritteen messageDigest ja suorittaa RSA PKCS#1 v1.5 -varmistuksen upotetulle allekirjoittajan varmenteelle palauttaen arvon svValid, kun dokumentin tavut ovat ehjät
Skenaario on arkinen, mutta panokset eivät. Vastapuoli palauttaa allekirjoitetun sopimuksen, työnkulkusi on arkistoitava se, ja joku kysyy ainoan kysymyksen, jolla on merkitystä: onko tämä lähettämämme dokumentti tavulleen sama ja allekirjoitettu sillä varmenteella, jolla väitetään? Tähän vastaaminen koodissa on allekirjoitustarinan varmistuspuoli. Allekirjoituspuolta, eli PAdES-allekirjoitusten luomista ja upottamista, käsitellään kumppaniartikkelissa PAdES-digitaalisten allekirjoitusten luomisesta HotPDF-komponentilla. Tämä artikkeli käsittelee toista suuntaa: PDF saapuu valmiiksi allekirjoitettuna, ja haluat ohjelmallisen päätöksen Acrobat-ohjelman vihreän valintamerkin kuvakaappauksen sijaan
Miten allekirjoitettu PDF todistaa, ettei sitä ole peukaloitu?
PDF-allekirjoitus suojaa tiedoston tiettyjä tavualueita, ei abstraktia käsitettä "dokumentista". Standardi ISO 32000-1 §12.8 määrittelee mekanismin: allekirjoituksen lomakekenttä sisältää hakemiston, jonka /Contents-tietue sisältää CMS SignedData -säiliön (RFC 5652) ja jonka /ByteRange-taulukko nimeää ne tarkat tiedoston alueet, jotka allekirjoitus kattaa pykälän §12.8.1 mukaisesti. Taulukko on luettelo kohdistus- ja pituuspareista, käytännössä kahdesta segmentistä: kaikki ennen /Contents-heksamerkkijonoa ja kaikki sen jälkeen. Allekirjoitusarvo ei voi kattaa itseään, joten tiedosto tiivistetään (hash) tuon aukon ympäriltä
Tästä rakenteesta seuraa koko API:a muovaava tekijä: varmistuksen on tiivistettävä alkuperäiset sarjoitetut tavut sellaisina kuin ne ovat levyllä. Jäsennetty objektimalli on tähän hyödytön, sillä jopa muuttamattoman dokumentin uudelleensarjoitus tuottaa erilaisia tavuja. Siksi HotPDF tekee varmistuksen lähdetiedostoa vasten, josta dokumentti ladattiin, tai toimittamaasi raakojen tavujen TStream-virtaa vasten — ei koskaan sen muistissa olevaa esitystä vasten
Allekirjoituksen metatietojen lukeminen ennen varmistamista
GetLoadedSignatureInfo jäsentää allekirjoitushakemiston ja sen CMS-säiliön koskematta yhteenkään dokumentin tavuun. Tämä tekee siitä sopivan ensimmäisen kutsun silloin, kun halutaan vain näyttää kuka allekirjoitti ja milloin. Allekirjoituskentät on indeksoitu nollasta alkaen lomakekenttien järjestyksessä, ja GetLoadedSignatureFieldCount kertoo kuinka monta niitä on. Palautettu THPDFSignatureInfo-tietue sisältää kentän nimen, /SubFilter-tunnisteen, allekirjoittajan varmenteen yleisen nimen (common name), subject- ja issuer-tunnisteet, sarjanumeron, voimassaolopäivät, allekirjoitusajan (allekirjoitetusta attribuutista jos läsnä, muussa tapauksessa hakemiston /M-tietueesta), tiivistealgoritmin nimen sekä /Reason-, /Location- ja /ContactInfo-merkkijonot. Sen Status-jäsen pysyy arvossa svNotVerified, mikä on rehellinen leima tilalle "jäsennetty, ei varmistettu"
var
Pdf: THotPDF;
Info: THPDFSignatureInfo;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('signed-contract.pdf');
for I := 0 to Pdf.GetLoadedSignatureFieldCount - 1 do
begin
Info := Pdf.GetLoadedSignatureInfo(I);
Writeln('Field: ', Info.FieldName);
Writeln('Signer: ', Info.SignerName);
Writeln('Issuer: ', Info.IssuerDN);
Writeln('Algorithm: ', Info.HashAlgorithm);
Writeln('SubFilter: ', Info.SubFilter);
end;
finally
Pdf.Free;
end;
end;
Kryptografisen tarkistuksen suorittaminen
VerifyLoadedSignatureEx suorittaa täyden varmistuksen tiedostosta ladatulle dokumentille ja palauttaa täytetyn tietueen yhdellä kutsulla: se avaa lähdetiedoston uudelleen, tiivistää /ByteRange-segmentit SignerInfo-tiivistealgoritmilla, vertaa tulosta messageDigest-attribuuttiin (RFC 5652 §5.4) ja suorittaa sen jälkeen RSA-varmistuksen allekirjoitetuille attribuuteille niiden DER SET -uudelleenkoodauksen yli. Jos allekirjoitus ei sisällä allekirjoitettuja attribuutteja, RSA-tarkistus suoritetaan suoraan dokumentin tiivisteen yli. Tuetut allekirjoitukset ovat RSA PKCS#1 v1.5 varustettuna SHA-1-, SHA-256-, SHA-384- tai SHA-512-tiivisteillä, mikä kattaa valtavirran allekirjoitustyökalujen tuottamat adbe.pkcs7.detached- ja ETSI.CAdES.detached-alisuodattimet
var
Status: THPDFSignatureVerifyStatus;
Info: THPDFSignatureInfo;
begin
Status := Pdf.VerifyLoadedSignatureEx(0, Info);
case Status of
svValid:
if Info.CoversWholeDocument then
Writeln('Valid; signature covers the whole file')
else
Writeln('Valid; file was extended after signing');
svDigestMismatch:
Writeln('Document bytes changed after signing');
svSignatureInvalid:
Writeln('RSA check failed over signed attributes');
svUnsupportedAlgorithm:
Writeln('Non-RSA key or unknown digest algorithm');
svMalformed:
Writeln('CMS container could not be parsed');
svSourceUnavailable:
Writeln('No source bytes; use the TStream overload');
end;
end;
Kaksi toteutuksen yksityiskohtaa on hyvä tietää, sillä ne selittävät ulospäin mystisiltä näyttäviä epäonnistumisia. Ensinnäkin allekirjoitettujen attribuuttien tarkistus on tarkka koodauksesta: tiedoston sisällä attribuutit on merkitty tunnisteella [0] IMPLICIT, mutta allekirjoitus laskettiin niiden DER SET OF -muodon yli. Siksi varmistaja merkitsee ne uudelleen ennen tiivistämistä, täsmälleen kuten RFC 5652 §5.4 vaatii. Omatekoinen varmistaja, joka tiivistää tavut sellaisenaan kuin ne esiintyvät tiedostossa, hylkää jokaisen oikein allekirjoitetun dokumentin. Toiseksi /Contents on perinteisesti nollatäytteinen varattua tavubudjettia varten, joten varmistaja katkaisee DER-blobin sen ulomman SEQUENCE-osan todelliseen pituuteen ennen jäsentämistä; roskalta näyttävät loppunollat ovat normaaleja, eivät korruptiota. Samaan ASN.1-jäsennyksen vaarojen perheeseen, varmenteen tuontipuolella, pureudutaan artikkelissa PKCS#12- ja ASN.1-tietoturvan vahvistamisesta HotPDF-komponentissa
Mitä voimassa oleva allekirjoitus todellisuudessa takaa?
svValid tarkoittaa täsmälleen tätä: /ByteRange-taulukon nimeämät tavut tiivistyvät arvoon, jonka allekirjoittaja allekirjoitti, ja allekirjoitus on varmistettu CMS-säiliöön upotetun varmenteen julkisella avaimella. Se takaa tavujen eheyden ja avainsidonnan, ei mitään muuta. Varmenteiden ketjutus ja luottamuksen varmistaminen jäävät HotPDF:n varmistajan ulkopuolelle: se ei käy läpi ketjua juurivarmenteeseen saakka, tarkista kumottuja varmenteita tai kysy tietoja luottamusvarastoista. Hyökkääjän itse allekirjoittama varmenne, jolla on allekirjoitettu muokattu dokumentti uudelleen, varmistuu tilaksi svValid, koska matematiikka on sisäisesti johdonmukaista. Se, onko allekirjoittaja se, joka hän väittää olevansa, ja pitäisikö häneen luottaa, on käytäntöpäätös, joka kuuluu erilliselle kerrokselle — olipa se sitten organisaatiosi varmenteiden sallittujen luettelo, Windowsin varmennesäilö tai varmennusviranomainen
CoversWholeDocument-lippu suojaa hienovaraisemmalta aukolta. Allekirjoitus kattaa aina vain oman /ByteRange-alueensa, ja PDF:n inkrementaalinen päivitysmekanismi sallii sisällön lisäämisen allekirjoituksen jälkeen mitätöimättä sitä. Tämä on suunniteltu ominaisuus, jonka avulla moniallekirjoituksen työnkulut toimivat. Lippu lasketaan varmistuksen aikana ja se on tosi vain silloin, kun kaksi segmenttiä ja /Contents-aukko kattavat koko tiedoston. Kun svValid saapuu siten, että CoversWholeDocument on epätosi, allekirjoitettu versio on ehjä, mutta tiedosto sisältää myöhempiä lisäyksiä, ja työnkulkusi tulisi päättää, sallitaanko näiden lisäysten tekemät muutokset
Virrasta ladatut ja salatut dokumentit tarvitsevat omat lähdetavut
Parametriton VerifyLoadedSignature ja VerifyLoadedSignatureEx riippuvat siitä, että komponentti muistaa, mistä tiedostosta dokumentti on peräisin. Jos lataat dokumentin virrasta (stream), uudelleenavattavaa tiedostonimeä ei ole; sama pätee salattujen dokumenttien salasanan uudelleenlatauspolun jälkeen, eli työnkulkuun, jota käsitellään artikkelissa AES-256-PDF-salauksesta HotPDF-komponentilla. Molemmissa tapauksissa tiedostopohjaiset ylikuormitukset palauttavat arvon svSourceUnavailable arvailun sijaan. Korjaus tähän on TStream-ylikuormitus, jonka avulla voit välittää alkuperäiset raa'at tavut mistä tahansa, missä niitä säilytät — tallessa olevasta tiedostosta, muistipuskurista tai tietokannan blob-kentästä
var
Src: TFileStream;
Status: THPDFSignatureVerifyStatus;
Info: THPDFSignatureInfo;
begin
// Stream-loaded document: the component holds no source
// file name, so supply the original bytes yourself.
Src := TFileStream.Create('signed-contract.pdf',
fmOpenRead or fmShareDenyWrite);
try
Status := Pdf.VerifyLoadedSignature(0, Src, Info);
if Status <> svValid then
Writeln('Verification failed: ', Ord(Status));
finally
Src.Free;
end;
end;
Sen raportointi, mitä ei voida varmistaa
Varmistaja, joka tuntee vain tilat "validi" ja "epävalidi", raportoi väärin dokumenteista, joita se ei vain ymmärrä. Siksi tilan luettelointi erottelee tapaukset, jotka käyttöliittymäsi tulisi erottaa toisistaan. svDigestMismatch tarkoittaa, että dokumentin tavut ovat muuttuneet allekirjoittamisen jälkeen, mikä on perinteinen peukalointisignaali. svSignatureInvalid tarkoittaa, että tavut tiivistyvät oikein, mutta RSA-tarkistus epäonnistui, mikä viittaa korruptoituneeseen tai väärennettyyn allekirjoitusarvoon. svUnsupportedAlgorithm on rehellinen vastaus ECDSA-avaimille ja tuntemattomille tiivisteille: allekirjoitus voi olla täysin kunnossa, HotPDF ei pysty varmistamaan sitä, ja sen raportointi "epävalidiksi" leimaisi ehjän dokumentin syyttä. svMalformed merkitsee CMS-säiliön, jota ei voitu jäsentää lainkaan. Porttityyppisissä tarkistuksissa VerifyAllLoadedSignatures palauttaa arvon tosi vain silloin, kun vähintään yksi allekirjoituskenttä on olemassa ja jokainen niistä varmistuu tilaksi svValid. Tämä on kätevä yksittäinen totuusarvo arkiston vastaanottoputkelle, joka hylkää kaiken muun
Allekirjoituksen varmistus, PAdES-allekirjoitus, AES-256-salaus ja ladatun dokumentin muokkaus-API toimitetaan kaikki samassa natiivissa VCL-kirjastossa Delphille ja C++Builderille ilman ulkoisia DLL-riippuvuuksia. Täydellinen ominaisuusluettelo ja tuetut IDE-versiot ovat saatavilla HotPDF Component -tuotesivulta