HotPDF preverja digitalne podpise v naloženih dokumentih PDF prek treh metod THotPDF: GetLoadedSignatureInfo, VerifyLoadedSignature in VerifyLoadedSignatureEx, uvedenih v različici v2.259.0. Komponenta ponovno zgošči segmente /ByteRange izvirne datoteke, preveri atribut CMS messageDigest in izvede preverjanje RSA PKCS#1 v1.5 v primerjavi z vgrajenim potrdilom podpisnika ter vrne svValid, ko so bajti dokumenta nedotaknjeni
Scenarij je vsakdanji, vložki pa ne. Nasprotna stranka vrne podpisano pogodbo, vaš delovni tok jo mora shraniti, nekdo pa zastavi edino vprašanje, ki je pomembno: ali je to dokument, ki smo ga poslali, bajt za bajtom, in ali je podpisan s potrdilom, ki ga navaja? Odgovor na to v kodi je stran preverjanja v zgodbi o podpisih; stran podpisovanja, torej gradnja in vgrajevanje podpisov PAdES, pa je obravnavana v spremljevalnem članku o ustvarjanju digitalnih podpisov PAdES s HotPDF. Ta članek govori o nasprotni smeri: PDF prispe že podpisan, vi pa želite programsko razsodbo namesto posnetka zaslona Acrobatove zelene kljukice
Kako podpisan PDF dokazuje, da vanj ni bilo poseženo?
Podpis PDF varuje določene bajtne obsege datoteke in ne nekega abstraktnega koncepta "dokumenta". ISO 32000-1 §12.8 določa mehanizem: polje obrazca podpisa vsebuje slovar, katerega vnos /Contents vsebuje vsebnik CMS SignedData (RFC 5652), njegovo polje /ByteRange pa navaja natančna območja datoteke, ki jih podpis pokriva, v skladu s §12.8.1. Polje je seznam parov odmikov in dolžin, v praksi dveh segmentov: vse pred heksadecimalnim nizom /Contents in vse po njem. Vrednost podpisa ne more pokrivati same sebe, zato se datoteka zgošča okoli te luknje
Ta zasnova ima posledico, ki oblikuje celoten API: preverjanje mora zgoščevati izvirne serializirane bajte natanko tako, kot ležijo na disku. Analiziran model objekta je za to neuporaben, saj ponovna serializacija celo nespremenjenega dokumenta ustvari drugačne bajte. HotPDF zato preverja glede na izvorno datoteko, iz katere je bil dokument naložen, ali glede na tok TStream surovih bajtov, ki jih zagotovite sami, nikoli pa glede na njegovo predstavitev v pomnilniku
Branje metapodatkov podpisa pred preverjanjem česar koli
Klic GetLoadedSignatureInfo analizira slovar podpisa in njegov vsebnik CMS, ne da bi se dotaknil enega samega bajta dokumenta, zaradi česar je to pravi prvi klic, ko želite le prikazati, kdo je podpisal in kdaj. Polja podpisov so indeksirana od 0 v vrstnem redu polj obrazca, klic GetLoadedSignatureFieldCount pa vam pove, koliko jih obstaja. Vrnjeni zapis THPDFSignatureInfo vsebuje ime polja, /SubFilter, splošno ime potrdila podpisnika, razločevalna imena prejemnika in izdajatelja (subject in issuer distinguished names), serijsko številko, datume veljavnosti, čas podpisovanja (iz podpisanega atributa, ko je prisoten, sicer iz vnosa /M v slovarju), ime algoritma zgoščevanja ter nize /Reason, /Location in /ContactInfo. Njegov član Status ostane svNotVerified, kar je poštena oznaka za "analizirano, ni preverjeno"
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;
Izvajanje kriptografskega preverjanja
Klic VerifyLoadedSignatureEx izvede popolno preverjanje za dokument, naložen iz datoteke, in vrne izpolnjen zapis s podatki v enem klicu: ponovno odpre izvorno datoteko, zgošči segmente /ByteRange z algoritmom zgoščevanja SignerInfo, primerja rezultat s podpisanim atributom messageDigest (RFC 5652 §5.4) in nato izvede preverjanje RSA nad ponovnim kodiranjem DER SET podpisanih atributov. Kadar podpis ne vsebuje podpisanih atributov, se preverjanje RSA izvede neposredno nad zgoščeno vrednostjo dokumenta. Podprti podpisi so RSA PKCS#1 v1.5 z zgoščenimi vrednostmi SHA-1, SHA-256, SHA-384 ali SHA-512, kar pokriva podfiltre adbe.pkcs7.detached in ETSI.CAdES.detached, ki jih ustvarijo glavna orodja za podpisovanje
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;
Dve podrobnosti implementacije sta vredni omembe, saj pojasnjujeta neuspehe, ki so od zunaj videti skrivnostni. Prvič, preverjanje podpisanih atributov je izbirčno glede kodiranja: znotraj datoteke so atributi označeni kot [0] IMPLICIT, vendar je bil podpis izračunan nad njihovo obliko DER SET OF, zato preverjalnik pred zgoščevanjem ponovno označi elemente, natanko tako, kot zahteva RFC 5652 §5.4. Doma narejen preverjalnik, ki zgošča bajte, kot se pojavljajo v datoteki, bo zavrnil vsak pravilno podpisan dokument. Drugič, polje /Contents je običajno dopolnjeno z ničlami do rezerviranega proračuna bajtov, zato preverjalnik pred analiziranjem skrajša blok DER na dejansko dolžino njegove zunanje SEQUENCE; odvečne končne ničle so običajne in ne pomenijo poškodbe. Enaka družina nevarnosti pri analizi ASN.1 na strani uvoza potrdil je tema članka o varnostnem utrjevanju PKCS#12 in ASN.1 v HotPDF
Kaj veljaven podpis dejansko zagotavlja?
Status svValid pomeni natanko to: bajti, navedeni v /ByteRange, se zgoščijo v vrednost, ki jo je podpisnik podpisal, podpis pa se preveri z javnim ključem potrdila, vgrajenega v vsebniku CMS. To je celovitost bajtov in vezava ključa ter nič drugega. Preverjanje verige potrdil in zaupanja sta izrecno izven obsega preverjalnika HotPDF: ne prehodi verige do korena, ne preveri preklica in ne preveri nobene shrambe zaupanja. Samopodpisano potrdilo napadalca, ki je ponovno podpisal spremenjen dokument, se bo preverilo kot svValid, ker je matematika interno skladna. Ali je podpisnik tisti, za katerega se izdaja, in ali bi mu morali zaupati, je odločitev o politiki, ki pripada ločeni plasti, najsi bo to seznam dovoljenih potrdil vaše organizacije, shramba potrdil Windows ali organ za potrjevanje
Zastavica CoversWholeDocument varuje pred subtilnejšo vrzeljo. Podpis vedno pokriva le svoj /ByteRange, PDF-jev mehanizem inkrementalnih posodobitev pa omogoča dodajanje vsebine po podpisu brez njegove razveljavitve, kar je namerno in omogoča delovanje delovnih tokov z več podpisi. Zastavica se izračuna med preverjanjem in je resnična le, ko dva segmenta in vrzel /Contents obsegajo celotno datoteko. Če status svValid pride z zastavico CoversWholeDocument, ki je napačna (false), je podpisana revizija nedotaknjena, vendar datoteka vsebuje kasnejše dodatke, vaš delovni tok pa se mora odločiti, ali bo te dodatke toleriral
Dokumenti, naloženi iz tokov in šifrirani dokumenti, potrebujejo lastne izvorne bajte
Klica VerifyLoadedSignature in VerifyLoadedSignatureEx brez parametrov sta odvisna od tega, ali si komponenta zapomni, iz katere datoteke je dokument prišel. Naložite dokument iz toka in ime datoteke za ponovno odpiranje ne bo na voljo; enako velja po poti ponovnega nalaganja z geslom, ki se uporablja za šifrirane dokumente, kar je delovni tok, opisan v članku o šifriranju PDF z AES-256 v HotPDF. V obeh primerih preobremenitve, podprte z datotekami, vrnejo svSourceUnavailable namesto ugibanja. Rešitev je preobremenitev TStream, ki vam omogoča, da predate izvirne surove bajte od tam, kjer ste jih shranili — datoteke, ki jo še imate, pomnilniškega odložišča ali podatkovne baze
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;
Poročanje o tistem, česar ne morete preveriti
Preverjalnik, ki pozna le "veljavno" in "neveljavno", bo napačno poročal o dokumentih, ki jih preprosto ne razume, zato enumeracija stanja ločuje primere, ki jih mora vaš uporabniški vmesnik razlikovati. svDigestMismatch pomeni, da so se bajti dokumenta po podpisovanju spremenili, kar je klasičen znak nepooblaščenega spreminjanja. svSignatureInvalid pomeni, da se bajti pravilno zgoščijo, vendar je preverjanje RSA spodletelo, kar kaže na poškodovano ali ponarejeno vrednost podpisa. svUnsupportedAlgorithm je pošten odgovor za ključe ECDSA in neznane algoritme zgoščevanja: podpis je lahko povsem dober, HotPDF ga preprosto ne more preveriti, poročanje o tem kot o "neveljavnem" pa bi škodovalo zdravemu dokumentu. svMalformed označuje vsebnik CMS, ki ga sploh ni bilo mogoče analizirati. Za preverjanja tipa vrat funkcija VerifyAllLoadedSignatures vrne true le, ko obstaja vsaj eno polje podpisa in se vsako od njih preveri kot svValid, kar je priročna enojna logična vrednost (boolean) za cevovod za prevzem arhivov, ki zavrača vse ostalo
Preverjanje podpisov, podpisovanje PAdES, šifriranje AES-256 in urejanje naloženih dokumentov so na voljo v isti izvorni knjižnici VCL za Delphi in C++Builder brez zunanjih odvisnosti od datotek DLL; celoten seznam funkcij in podprtih različic IDE je na strani izdelka HotPDF Component