Ön kapott egy aláírt PDF-et, és meg kell mutatnia a megjelenítőjében, hogy ki írta alá, mikor írták alá, hogy az aláírás kiterjed-e a teljes fájlra, és milyen szinten áll a hosszú távú megfelelőség szempontjából. A Delphihez és Lazarushoz készült PDFium Component mind a négy kérdésre választ ad írásvédett hívásokkal: az FPDF_GetSignatureCount és a FPDFSignatureObj_* család feltárja az aláírási szótárt (signature dictionary), a TPdf.ValidatePades pedig osztályozza a PAdES alapvonal-szintet (baseline level). Ez az első a PDFium-mal történő PDF-aláírásokról szóló három cikk közül; az ezt követő kettő a B-B aláírás létrehozásával és a hosszú távú időbélyegek hozzáadásával foglalkozik. Egy határt azonban már az elején le kell fektetni: itt minden vizsgálat, és az aláírás állításainak elolvasása egészen más feladat, mint a kriptográfiájának ellenőrzése vagy a döntés arról, hogy bízik-e az aláíróban
Miért nem csak egy bájt-halmaz a PDF aláírási szótár?
A PDF aláírás egy szótár (dictionary), nem pedig egy átlátszatlan melléklet, és a kél legfontosabb bejegyzése megmondja, hogy a fájl mekkora része van valójában védve. Az ISO 32000-1 §12.8 szabvány az aláírási szótárt a /ByteRange és a /Contents bejegyzésekkel definiálja. A /Contents egy hexadecimálisan kódolt CMS SignedData struktúrát (RFC 5652) tartalmaz – ez a kriptográfiai boríték, amely az aláíró tanúsítványát, az aláírt attribútumokat és magát az aláírási értéket hordozza. A /ByteRange az a rész, amelyet a fejlesztők alábecsülnek: ez két eltolás-hosszúság (offset-length) szakaszból álló tömb, amely a teljes fájlt lefedi, kivéve a /Contents hexadecimális karakterláncot. Ez a rés pontosan az a hely, ahol az aláírás bájtjai ülnek, és a két oldalán lévő két szakasz pontosan az, amelyre az aláírás kötelezettséget vállal
A ByteRange kialakítása az, ami auditálhatóvá teszi az inkrementális mentést. Mivel az aláíró nem tudja hashelni a még nem létező aláírásbájtokat, a fájl fel van osztva a /Contents helyőrző körül, és minden mást behashelnek az aláírásba. Az az aláírás, amelynek ByteRange-e nem éri el a fájl végét, figyelmeztető jel: a lefedett tartomány után, egy későbbi inkrementális frissítéssel hozzáfűzött tartalom nem törné meg az aláírást, annak ellenére sem, hogy megváltoztatja azt, amit az olvasó lát. Tehát a komoly vizsgáló elsőként nem azt ellenőrzi, hogy ki írta alá, hanem azt, hogy az aláírás lefedi-e azokat a bájtokat, amelyeket támogatni látszik
Az aláírási szótár olvasása a PDFium írásvédett API-jával
A PDFium Component két írásvédett tagon keresztül teszi elérhetővé az aláírási szótárt: ezek a SignatureCount és a Signature[Index] rekord. A színfalak mögött ezek az FPDF_GetSignatureCount, FPDF_GetSignatureObject és az FPDFSignatureObj_* elérési metódusokat hívják meg a /SubFilter, /ByteRange, /Contents, /Reason és az aláírási idő lekérésére. Az írásvédettség a kulcsszó itt: a PDFium meg tudja vizsgálni és olvasni az aláírásokat, de nincs API-ja azok létrehozására vagy írására, ezért a sorozat aláíró oldala inkább maga a könyvtár által van megvalósítva, nem pedig a PDFium által
var
Pdf: TPdf;
i: Integer;
Sig: TPdfSignature;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'contract-signed.pdf';
Pdf.Active := True;
for i := 0 to Pdf.SignatureCount - 1 do
begin
Sig := Pdf.Signature[i];
Writeln('SubFilter : ', Sig.Encoding); // ETSI.CAdES.detached, adbe.pkcs7.detached, ...
Writeln('Signed at : ', Sig.Time); // signer date string, e.g. D:20260708120000+02'00'
Writeln('Reason : ', Sig.Reason);
Writeln('CMS length: ', Length(Sig.Content)); // raw DER SignedData taken from /Contents
Writeln('DocMDP : ', Sig.Permission); // 0 = no certification, 1..3 = MDP level
end;
finally
Pdf.Free;
end;
end;
Mindegyik TPdfSignature rekord közvetlenül leképeződik a szótárra. A Encoding a /SubFilter, a leginkább diagnosztikus mező, mert megnevezi az aláírás-kezelőt, és azonnal elválasztja a modern ETSI.CAdES.detached aláírást a régitől vagy a tiltottól. A Time az aláíró által bejelentett aláírási idő PDF dátum-karakterláncként, ami az aláíró állítása, és nem hiteles idő. A Content a nyers CMS SignedData, a Permission pedig a DocMDP tanúsítási szintet mutatja meg (0 a szokásos jóváhagyási aláíráshoz, 1-3 a későbbi módosításokat korlátozó tanúsítási aláíráshoz). Az egyetlen mező, amelyet a rekord nem mutat meg, a ByteRange eltolási tömbje, és ez a kihagyás szándékos, mivel a ValidatePades elvégzi a lefedettségi számításokat ahelyett, hogy azt kézzel kellene újra megírnia
Mi a különbség a PAdES B-B, B-T, B-LT és B-LTA szintek között?
A négy PAdES alapvonali szint egy létrát alkot a vizsgált alap aláírástól az évtizedes archiválást túlélő aláírásig, és minden szint szigorúan tartalmazza az alatta lévőt. Az ETSI EN 319 142-1 szabvány B-B, B-T, B-LT és B-LTA szintekként határozza meg őket. A B-B (Basic) az aláírás plusz a kötelező aláírt attribútumok, és semmi több. A B-T (Timestamp) egy megbízható RFC 3161 időbélyeget ad az aláíráshoz, így az aláírás pillanatát egy időbélyegző hatóság tanúsítja, ahelyett, hogy az aláíró órája állítaná azt be. A B-LT (Long-Term) beágyazza az ellenőrzési anyagot – a tanúsítványláncot, és opcionálisan az OCSP vagy CRL válaszokat – a fájlba, így az aláírás évekkel később is ellenőrizhető marad, amikor a kibocsátó infrastruktúra már nem létezik. A B-LTA (Long-Term with Archive timestamp) egy dokumentum-időbélyegzővel veszi körül ezt az anyagot, védve magát a hosszú távú adatot, és lehetőséget adva az időbélyegzés megismétlésére, mielőtt a mögöttes kriptográfia elavulna
A gyakorlati olvasat az időhorizontról szól. A B-B aláírás arra válaszol, hogy „aláírta-e ezt valaki”. A B-T arra, hogy „és mikor, bizonyíthatóan”. A B-LT arra, hogy „és ellenőrizhetem-e még a tanúsítványok lejárta után is”. A B-LTA pedig arra, hogy „és ez az ellenőrzés húsz év múlva is megállja-e a helyét”. A szabályozási profilok kiválasztanak egy szintet: sok e-számlázási és eIDAS környezet legalább B-T szintet követel meg, az archiválási előírások pedig a B-LT vagy B-LTA szintet célozzák meg. A vizsgálat lényege annak kiderítése, hogy a dokumentum valójában melyik szintet éri el, még mielőtt elfogadná vagy elutasítaná azt
Az alapvonali szint észlelése a TPdf.ValidatePades segítségével
A PDFium Component a teljes szintkérdést egyetlen hívásra redukálja. A TPdf.ValidatePades egy TPadesValidationResult rekordot ad vissza, amelynek Level mezője egy TPadesLevel – plNone, plUnknown, plB_B, plB_T, plB_LT vagy plB_LTA –, a problémák halmaza, az aláírások száma és a dokumentum-időbélyegek száma mellett. A szint következtetése monoton történik: a vizsgáló először a B-B-t állapítja meg, majd B-T-re lépteti elő, ha van aláírás-időbélyeg vagy dokumentum-időbélyeg, B-LT-re, ha a katalógus hordoz egy /DSS-t tanúsítványokkal és a /Extensions /ESIC Level 1 jelölőt, és B-LTA-ra, ha a dokumentum-időbélyeg és az ESIC Level 2 jelölő is jelen van. Két segédfunkció teszi a választ használhatóvá: az IsCompliant csak akkor True, ha a szint eléri legalább a B-B-t és a problémák halmaza üres, az IsCompliantAt pedig lehetővé teszi egy szabályozási alsó korlát, például a plB_T kikényszerítését
var
Pdf: TPdf;
R: TPadesValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'contract-signed.pdf';
Pdf.Active := True;
R := Pdf.ValidatePades;
case R.Level of
plNone: Writeln('No PAdES signature present');
plUnknown: Writeln('Signature present but level undeterminable');
plB_B: Writeln('PAdES B-B (basic)');
plB_T: Writeln('PAdES B-T (trusted timestamp)');
plB_LT: Writeln('PAdES B-LT (long-term material embedded)');
plB_LTA: Writeln('PAdES B-LTA (archive timestamp)');
end;
Writeln('Signatures : ', R.SignatureCount);
Writeln('DocTimeStamps: ', R.DocTimeStampCount);
if R.IsCompliantAt(plB_T) then
Writeln('Meets the B-T policy floor')
else
Writeln('Below the required B-T level');
finally
Pdf.Free;
end;
end;
Miért tiltott SubFilter az adbe.pkcs7.sha1?
Mivel a SHA-1 feltört, és az adbe.pkcs7.sha1 kezelő fixen beépíti azt. Ez a SubFilter a dokumentumot SHA-1 algoritmussal hasheli elő a PKCS#7-be burkolás előtt, a SHA-1 pedig évek óta ütközés-sebezhető (collision-vulnerable), ezért az EN 319 142-1 szabvány 6.3-as pontja egyértelműen tiltja az alapvonali aláírásoknál. A ValidatePades a ppeiForbiddenSubFilter hibát váltja ki, ha adbe.pkcs7.sha1 vagy adbe.x509.rsa_sha1 értéket lát, és a ppeiBadDigestAlgorithm hibát, ha maga a CMS az MD5-öt vagy a SHA-1-et használja üzenet-kivonatként (digest, 6.2.1-es pont). Ez két különböző ellenőrzés, amely ugyanazt a gyengeség-osztályt csípi el két különböző rétegben
A problémák halmazának összesen 26 tagja van, és azokkal, amelyekkel leggyakrabban találkozik, a szerkezet és a lefedettség körül csoportosulnak. A ppeiByteRangeNotCoveringFile a korábban leírt lefedettség-ellenőrzés. A ppeiForbiddenCertKey akkor váltódik ki, ha az aláírási szótár egy /Cert bejegyzést hordoz, amit a PAdES tilt, mert a láncnak ehelyett a CMS SignedData.certificates belsejében kell élnie. A ppeiMissingSigningCertificate, ppeiMissingContentType és ppeiMissingMessageDigest a hiányzó kötelező aláírt attribútumokat jelzi, a ppeiDetachedContentViolation pedig olyan aláírást kap el, amely hibásan beágyazza az aláírt tartalmat ahelyett, hogy leválasztaná azt. A halmaz felsorolása a puszta elutasítást egy naplózható diagnózissá alakítja
var
R: TPadesValidationResult;
Issue: TPadesValidationIssue;
begin
R := Pdf.ValidatePades;
if R.Issues <> [] then
for Issue := Low(TPadesValidationIssue) to High(TPadesValidationIssue) do
if Issue in R.Issues then
Writeln('Issue: ',
GetEnumName(TypeInfo(TPadesValidationIssue), Ord(Issue)));
end;
Amit a ValidatePades nem ellenőriz
A ValidatePades a szerkezetet ellenőrzi, nem a bizalmat, és a kettő összetévesztése veszélyes hiba. A plB_LTA eredmény azt jelenti, hogy a dokumentum tartalmaz egy jól formázott B-LTA aláírást, az összes előírt attribútummal, anyaggal és időbélyegzővel a megfelelő helyen – ez nem jelenti azt, hogy az aláírás kriptográfiailag érvényes, hogy a tanúsítványlánc egy Ön által megbízható gyökérhez vezet, vagy hogy a láncban lévő egyetlen tanúsítványt sem vonták vissza. A vizsgáló szándékosan nem végez kriptográfiai ellenőrzést: nem számítja ki újra az aláírást a ByteRange felett, nem építi fel és nem értékeli ki a bizalmi láncot (trust chain), és nem ellenőrzi az OCSP vagy CRL visszavonási állapotot. Ez a megosztás szándékos és hasznos, mivel a strukturális vizsgálat gyors, teljesen determinisztikus, és nem igényel kulcsokat, hálózatot és platform-kriptót, így a ValidatePades azonosan fut Windows, Linux és macOS rendszereken, mint tiszta Pascal kód a fájlbájtok felett. A bizalmi lánc ellenőrzése ezzel szemben elválaszthatatlan a szabályozástól – melyik gyökérben bízik, hogyan kéri le a visszavonásokat, mennyire szigorú az időbélyegző-tolerancia –, és egy későbbi szakaszhoz tartozik, amely a platform tanúsítványtárától függ. Ezért kezelje a sikeres ValidatePades eredményt szükséges kapuként, amely igazolja, hogy az aláírás megfelelően formázott, majd adjon át egy szerkezetileg ép aláírást a valódi kriptográfiai ellenőrzésnek, mielőtt hagyatkozna rá
Ez a strukturális ellenőrzés a megfelelő kiindulópont, és természetes módon párosul a PDF biztonsági kockázatainak a PDFium Component-tel végzett auditálásával, valamint olyan formátum-megfelelőségi munkákkal, mint a nyomtatásra kész PDF/X dokumentumok ellenőrzése. Miután képes elolvasni és osztályozni az aláírást, a következő lépés az aláírás előállítása: a sorozat második cikke a PDF-ek aláírását tárgyalja PAdES B-B szabvánnyal, a harmadik pedig ezt terjeszti ki a megbízható időbélyegzőkre, valamint a hosszú távú B-LT és B-LTA aláírásokra. Az itt bemutatott írásvédett aláírás-vizsgálat és a ValidatePades osztályozó a Delphi, C++Builder és Lazarus rendszerekhez készült PDFium Component részét képezik