Egyetlen PAdES-aláírás ellenőrzése három független dolog vizsgálatát jelenti, és a megjelenítőben látható zöld pipa csak a harmadikról mond bármit. Először is a /ByteRange tömbnek a helyes bájtokat kell lefednie: az általa megnevezett szakaszoknak pontosan azt a bemenetet kell visszaállítaniuk, amelyre a CMS-lenyomat készült, úgy, hogy egyetlen aláírt bájt se maradjon rajtuk kívül. Másodszor a CMS-en belüli tanúsítványnak olyan gyökérhez kell láncolódnia, amelyben megbízik, és hordoznia kell azt az aláírt signing-certificate attribútumot, amelyet a PAdES megkövetel. Harmadszor, ha a profil időbélyeget állít, egy RFC 3161 tokennek kell az aláírásértéket a tanúsítvány lejárta előtti időponthoz kötnie. Az Acrobat mind a hármat egyetlen ikonba sűríti; egy megfelelőségi ellenőrző külön tartja őket, és így kell tennie annak a kódnak is, amely ezeket a fájlokat előállítja. A losLab PDF Library (PDF Library for Delphi) az aláírási oldalt, az időbélyeg újbóli beágyazását és azokat az ellenőrző hívásokat adja, amelyekkel egy ByteRange megvizsgálható, mielőtt megbízna benne
Egy megkülönböztetés szinte minden első PAdES-megvalósítást elgáncsol, ezért érdemes bármilyen kód előtt kimondani. Az /SubFilter /adbe.pkcs7.detached értékkel írt aláírás tökéletesen szabályos ISO 32000-1 §12.8 szerinti aláírás, amelyet az Acrobat érvényesnek jelent. Ugyanakkor nem PAdES-aláírás, mert az ETSI EN 319 142-1 minden alapszinten ETSI.CAdES.detached értéket követel meg. Egy eIDAS megfelelőségi ellenőrző az elsőt elutasítja, a másodikat elfogadja, noha a kriptográfia azonos. A profil olyan állítás, amelyet a dokumentum önmagáról tesz, és ennek az állításnak a helyre tétele egyetlen hívás a PDF Library for Delphi alatt
Mitől lesz egy PDF-aláírásból PAdES-aláírás?
Az ETSI EN 319 142-1 négy alapszintet határoz meg a CMS formátumra építve. A PAdES-B-B a belépési pont: CAdES-aláírás egy PDF-aláírásmezőben, ETSI.CAdES.detached SubFilter értékkel és aláírt signing-certificate attribútummal. A PAdES-B-T egy RFC 3161 időbélyeggel egészíti ki az aláírásértéket, bizonyítva, hogy az aláírás egy visszadátumozhatatlan időpont előtt már létezett. A PAdES-B-LT az ellenőrzéshez szükséges tanúsítványokat, CRL-eket és OCSP-válaszokat egy Document Security Store tárba ágyazza, így a fájl akkor is ellenőrizhető marad, amikor a kibocsátó hitelesítésszolgáltató már leállította az infrastruktúráját. A PAdES-B-LTA olyan dokumentum-időbélyeggel zárja a sort, amely az algoritmusok gyengülésével újravédi az összegyűlt bizonyítékokat
A PDF Library for Delphi ezeket a fogalmakat az aláírási folyamat API-jára képezi le. A profiljelölő a SetSignProcessCustomSubFilter. Ha a szabályzata kötelezettségtípus-jelzést kíván (származási bizonyíték, jóváhagyási bizonyíték vagy az 1-től 6-ig számozott többi ETSI-azonosító egyike), az a SetSignProcessCommitmentType hívásán megy át. Kifejezett aláírási szabályzat a SetSignProcessSignaturePolicy hívással csatolható, amely a szabályzat OID-jét és annak lenyomatát várja. Egy alapértelmezés figyelmet érdemel: ha a lenyomatoló algoritmust automatikus értéken hagyja, a könyvtár SHA-256 értéket választ az ETSI és az adbe.pkcs7.detached aláírásokhoz, és csak az örökölt adbe.pkcs7.sha1 úton esik vissza SHA-1 értékre. Mégis állítsa be kifejezetten. Az auditorok megkérdezik, melyik hasítófüggvényt használta, és a kódban szereplő kifejezett érték könnyebben védhető, mint egy alapértelmezés, amelynek magyarázatához a kézikönyvhöz kell nyúlnia
Az alapszintű aláírás előállítása
A lapos API egylövetű állapotgépként vezényli az aláírást: nyisson folyamatot a forrásfájlon, állítsa be, zárja le egy kimeneti fájlba, majd olvassa ki az eredménykódot. Az alábbi sorozat SHA-256 lenyomattal készít PAdES-B-B aláírást. A legfontosabb sornak semmi köze magához az aláíráshoz. Ez a szándékosan túlméretezett /Contents helyfoglalás, mert ez az az egyetlen dolog, amelyen később nem tud változtatni, ha valaha időbélyeget kell tenni erre az aláírásra
var
Pdf: TPDFlib;
SignId: Integer;
begin
Pdf := TPDFlib.Create;
try
SignId := Pdf.NewSignProcessFromFile('invoice.pdf', '');
if SignId = 0 then
raise Exception.Create('cannot open source PDF');
Pdf.SetSignProcessField(SignId, 'Sig1');
Pdf.SetSignProcessPFXFromFile(SignId, 'company.pfx', PfxPassword);
Pdf.SetSignProcessInfo(SignId, 'Approved', 'Vienna', 'billing@example.com');
Pdf.SetSignProcessCustomSubFilter(SignId, 'ETSI.CAdES.detached');
Pdf.SetSignProcessDigestAlgorithm(SignId, 2); // SHA-256
Pdf.SetSignProcessReserveContentsBytes(SignId, 8192); // hely a későbbi időbélyegnek
Pdf.EndSignProcessToFile(SignId, 'invoice-signed.pdf');
if Pdf.GetSignProcessResult(SignId) <> 1 then
raise Exception.CreateFmt('signing failed, code %d',
[Pdf.GetSignProcessResult(SignId)]);
Pdf.ReleaseSignProcess(SignId);
finally
Pdf.Free;
end;
end;
A NewSignProcessFromFile 0 értéket ad vissza, amikor a forrást egyáltalán nem lehet megnyitni. Ezután a GetSignProcessResult választja szét azokat a hibamódokat, amelyek éles üzemben tényleg előfordulnak: a 4 rossz PDF-jelszót jelent, a 7 rossz PFX-jelszót, a 9 privát kulcs nélküli tanúsítványfájlt, a 10 nem írható kimeneti útvonalat, a 11 pedig az aláírásbájtok alkalmazása közbeni hibát. Ha a számkódot a bemeneti fájl neve mellé naplózza, egy ködös támogatási hibajegyből egyperces diagnózis lesz
Az RFC 3161 időbélyeg hozzáadása, amelyet a könyvtár nem hoz le Ön helyett
A PDF Library for Delphi nem szállít TSA-klienst, és ez szándékos határvonal, nem hiányosság. A könyvtár kiszámítja azt a hasítóértéket, amelyet az időbélyeg-hatóságnak ellenjegyeznie kell, majd utólag újra beágyazza a kibővített CMS-t; a HTTP-váltás és a közte lévő CMS-műtét a hívóé. A szétválasztásnak kemény technikai oka van. Az a Windows CryptoAPI vezérlő, amely névlegesen aláíratlan attribútumokat ad hozzá, a CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR, CRYPT_E_INVALID_INDEX hibával hasal el azon a leválasztott SignedData elrendezésen, amelyet a PAdES használ. Tehát a kibővített CMS-nek olyan CMS-kódolóból kell származnia, amely az Ön kezében van. Egyetlen könyvtár sem tudja csendben, egyetlen rendszerhívással behajtani a tokent, és amelyik ezt állítja, az valahol olyan helyen végzi a műtétet, ahová nem lát be
var
Pdf: TPDFlib;
StsId: Integer;
HashHex, TstDer, TsAttr, AugmentedCms: AnsiString;
begin
Pdf := TPDFlib.Create;
try
StsId := Pdf.NewPAdESSignatureTimeStampProcessFromFile('invoice-signed.pdf', '');
Pdf.SetPAdESSignatureTimeStampField(StsId, 'Sig1');
Pdf.SetPAdESSignatureTimeStampDigestAlgorithm(StsId, 2);
HashHex := Pdf.GetPAdESSignatureValueHashHex(StsId);
// az alábbi két hívás alkalmazáskód: egy HTTP POST a saját TSA-jához,
// és egy CMS-újrakódolás, amely aláíratlan attribútumként csatolja a tokent
TstDer := RequestTimeStampToken(HashHex);
TsAttr := Pdf.BuildPAdESSignatureTimeStampAttribute(TstDer);
AugmentedCms := AttachUnsignedAttribute(Pdf.GetPAdESSignatureCMSBytes(StsId), TsAttr);
Pdf.SetPAdESSignatureCMSBytes(StsId, AugmentedCms);
Pdf.EndPAdESSignatureTimeStampProcessToFile(StsId, 'invoice-bt.pdf');
if Pdf.GetPAdESSignatureTimeStampProcessResult(StsId) <> 1 then
raise Exception.Create('timestamp embedding failed');
Pdf.ReleasePAdESSignatureTimeStampProcess(StsId);
finally
Pdf.Free;
end;
end;
Figyelje itt is az eredménykódokat: a 12 azt jelenti, hogy a megnevezett aláírásmező nem létezik, a 11 azt, hogy a meglévő CMS nem volt elemezhető, a 13 pedig azt, hogy a kibővített CMS már nem fér el a lefoglalt /Contents helykitöltőben. A 13-as fáj igazán, mert az egyetlen javítás az újbóli aláírás: egy szokásos időbélyeg-token a tanúsítványláncával 4-6 KB, és a B-B lépésben tett 8192 bájtos foglalás pontosan azért van, hogy ennek a lépésnek legyen hová érkeznie
Az ellenőrzés a ByteRange szakasznál kezdődik, nem a tanúsítványláncnál
A megjelenítőben látható zöld pipa az adott gép tanúsítványtárával szembeni bizalmi döntés, nem a fájl szerkezetéről szóló ítélet. A programozott ellenőrzésnek lejjebb kell kezdődnie, azzal a kérdéssel, amelyet a növekményes frissítések tesznek finommá: pontosan mely bájtokat fedi le az egyes aláírások? Minden itt tárgyalt kiegészítés — legyen az második aláírás, DSS-szótár vagy dokumentum-időbélyeg — növekményes frissítéssel érkezik, és mindegyik frissítés a korábbi aláírás /ByteRange tartományán kívülre fűz bájtokat. Ezek a hozzáfűzött bájtok jogosak. Egy ellenőrzőnek mégis be kell sorolnia őket a dokumentum módosítási szabályzata alapján, a mezőnkénti DocMDP szint pedig, amelyben ez a szabályzat él, a GetSignatureDocMDPLevelByName hívással olvasható
var
Doc: TPDFlibSignDoc;
Names: TStringList;
I: Integer;
B0, B1, B2, B3, FileSize: Int64;
begin
FileSize := TFile.GetSize('invoice-bt.pdf'); // Open előtt: a SignDoc megosztási zárat tart
Doc := TPDFlibSignDoc.Create;
try
if not Doc.Open('invoice-bt.pdf', '', False) then
raise Exception.Create('cannot open for audit');
Names := TStringList.Create;
try
Doc.GetSignatureFieldNames(Names);
for I := 0 to Names.Count - 1 do
if Doc.GetSignatureValueObjNum(Names[I]) > 0 then // a >0 azt jelenti: tényleg aláírt
begin
B0 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 11)));
B1 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 12)));
B2 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 13)));
B3 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 14)));
if (B0 = 0) and (B2 + B3 = FileSize) then
Writeln(Names[I], ': covers the file to EOF')
else
Writeln(Names[I], ': earlier revision, or unexpected ByteRange layout');
end;
finally
Names.Free;
end;
Doc.Close;
finally
Doc.Free;
end;
end;
Két csapda él ezen az ellenőrzési úton. A TPDFlibSignDoc.Open kizárólagos megosztási zárral fogja a fájlt, tehát annak az ellenőrzőnek, amely a CMS-igazoláshoz a nyers fájlbájtokat is hasítani akarja, a vizsgálati megnyitás előtt kell beolvasnia a fájlt a memóriába. Fordítsa meg ezt a sorrendet, és az olvasás a saját maga által beállított záron hasal el. A második csapda inkább néma, mint hangos: a lapos API megfelelője, a GetSignProcessByteRange, Integer típust ad vissza, miközben az alatta lévő eltolások Int64 típusúak, tehát 2 GB fölött a lapos hívás panasz nélkül csonkol, és éppen ezért húzza ez a példa az ellenőrző osztályon át az eltolásokat. Egy hiányt is érdemes néven nevezni. A lapos rétegben egyáltalán nincs VerifySignature burkoló. A kriptográfiai ítéletek az osztályszintű TPDFlibSignatureVerifier osztálytól jönnek, amely vsValid, vsInvalid vagy vsUnknown értéket ad vissza, vagy egy külső ellenőrzőtől, amelyben a megfelelőségi szabályzata már megbízik
Hosszú távú ellenőrzés: DSS, VRI és a dokumentum-időbélyeg
A PAdES-B-LT azért létezik, mert a visszavonási infrastruktúra halandó. Az ETSI EN 319 142-1 §5.4.2.2 írja le a Document Security Store tárat: dokumentumszintű szótárat, amely tanúsítványokat, CRL-eket és OCSP-válaszokat hordoz, és aláírásonként is indexelhető VRI bejegyzésekkel, amelyek kulcsa az egyes aláírások /Contents értékének hasítója. A PDF Library for Delphi folyamata az időbélyeges tervezést tükrözi. A NewPAdESDSSProcessFromFile nyitja a folyamatot; az AddPAdESDSSCertificate, az AddPAdESDSSCRL és az AddPAdESDSSOCSP DER-blobokat fogad; az AddPAdESDSSVRI a kiválasztott anyagot egyetlen aláíráshoz köti; az EndPAdESDSSProcessToFile pedig növekményes frissítésként írja ki az egészet. A nehezén az Ön oldala marad. A visszavonási anyag lehozása és annak megítélése, elég friss-e ahhoz, hogy érdemes legyen beágyazni, a hívó dolga. A könyvtár azt garantálja, hogy a szótárak szerkezetileg megfelelők; azt nem tudja garantálni, hogy az OCSP-válaszadója igazat mondott
Az archiválási végpont, a B-LTA, dokumentum-időbélyeget ad hozzá: külön aláírásmezőt, amelynek típusa Sig helyett DocTimeStamp, és a SetSignProcessDocTimeStamp hívással készül lefoglalt aláíráshosszal. Ez nem váltja ki a B-T lépésből származó aláírás-időbélyeget. Az aláírás-időbélyeg azt bizonyítja, mikor létezett egy adott aláírás; a dokumentum-időbélyeg a teljes fájlt védi, a DSS-bizonyítékokkal együtt, és ez az az elem, amelyet egy hosszú távú archívum néhány évente megújít, ahogy az algoritmusok gyengülnek. Egy érett archiválási profil mindkettőt hordozza. Az ezeket a szerkezeteket megelőző olvasók kedvéért a TPDFlibSignDoc.EnsurePAdESExtensions az ESIC fejlesztői kiterjesztést rögzíti a dokumentumkatalógusban, jelezve, hogy a fájl ETSI által meghatározott képességeket használ
Egy reakciót érdemes elébe menni, mert hibának látszik, pedig nem az. Egy megjelenítő gyakran „ismeretlen érvényesség” állapotot jelent olyan fájlra, amelynek PAdES-szerkezete teljesen helyes. A bizalom és a szerkezet egymástól független tengelyek. A megjelenítő egyszerűen nem tudja az aláírót az adott gépen megbízhatónak tartott gyökérhez láncolni, ami magán hitelesítésszolgáltatóknál és tesztanúsítványoknál mindennapos, miközben a ByteRange-ellenőrzés és a CMS-igazolás egyaránt átmegy. A megoldás a gyökértanúsítvány rendes terjesztése, vagy az EU megbízható listáival szembeni kiértékelés, ha valóban a minősített eIDAS státusz a cél — nem az aláírási kód piszkálása
Az ellenőrzési oldal szemszögéhez, vagyis az aláírásmezők korpuszszintű felsorolásához, a ByteRange elrendezések kiírásához és a DocMDP szintek tömeges olvasásához lásd a társcikket a megfelelőségi és aláírási műhelyről. Azok az aláírt dokumentumok, amelyeknek archiválási szabályzatnak is meg kell felelniük, a PDF/A és PDF/UA előellenőrzés Delphiben cikkben leírt munkafolyamatba tartoznak. A teljes API-dokumentáció és a kiértékelési letöltések a losLab PDF Library for Delphi termékoldalán találhatók