A PDFium VCL a CMS hitelesítést cserélhető backendként kezeli az IPdfCmsVerifier interfész mögött, így a PAdES validátor futhat Windowson CryptoAPI-val, macOS-en a Keychainnel, és bárhol, ahol OpenSSL érhető el, a ConfigureSslCmsVerifier révén. Az interfész kicsi. Három OpenSSL viselkedés ez alatt magabiztos, téves válaszokat produkál, ha naivan implementálja
A motiváció egyértelmű, amint egy Delphi alkalmazás elhagyja a Windowst. Az aláírásvalidáció azon kevés területek egyike, ahol a platform kriptográfiai stackje nem implementációs részlet: az dönti el, mely tanúsítványok számítanak megbízhatónak, mely algoritmusok léteznek, és mit jelent a visszavonás. Bedrótozza az egyiket, és a kód nem portolható. Elrontja az absztrakciót, és minden platform más formájú választ ad, amelyeket a hívó nem tud összehasonlítani
Mit kell valójában hordoznia az absztrakciónak
Két hitelesítési forma és három független verdikt. A PDF aláírás detached: az aláírt tartalom a /Contents lyuk két oldalán elhelyezkedő két bájttartomány, tehát a VerifyDetached két szegmenst kap, nem egy puffert. Az időbélyeg token attached, saját tartalommal utazik, tehát a VerifyAttached csak a DER-t kapja
Az eredmény három státuszra bomlik, mert három különböző kérdésre válaszolnak, és ellentmondhatnak egymásnak. A SignatureStatus azt mondja meg, hogy a bájtokat aláírta-e az aláíró tanúsítványban lévő kulcs. A TrustStatus azt, hogy az a tanúsítvány láncol-e olyanhoz, amelyben megbízik. A RevocationStatus azt, hogy a tanúsítvány a releváns időpontban még érvényes volt-e. Egy dokumentum, amelynek aláírása matematikailag tökéletes, de egy olyan tanúsítványtól származik, amelyről sosem hallott, érvényes, megbízhatatlan és ismeretlen egyszerre, és ezt egyetlen booleanba összecsukni az, ahogy a validátorok végül hazudnak a felhasználóknak
uses
FPdfCrypto, FPdfCryptoSsl;
var
Options: TPdfCmsVerifyOptions;
begin
if not SslAvailable then
raise Exception.Create('libcrypto not usable: ' + SslMissingSymbols);
ConfigureSslTrustAnchors(LoadCorporateRoots); // DER, lehet üres
ConfigureSslCrls(LoadFreshCrls); // DER, lehet üres
ConfigureSslCmsVerifier; // telepíti a backendet
Writeln('backend : ', PadesCmsVerificationBackendName);
Writeln('library : ', SslLibraryPath, ' ', SslLibraryVersion);
Writeln('ABI : ', SslAbiLayout); // ulong=<n> long=<n>
Options := TPdfCmsVerifyOptions.Default;
Options.CheckRevocation := True;
Options.CollectChainCertificates := True;
end;
A SslAbiLayout kuriozitásnak tűnik, és nem az. Minden OpenSSL hibakód és minden tárflag C unsigned long-ként kel át a határon, amely négy bájt Windowson és nyolc Linuxon és macOS-en. Deklarálja fix 32 bites típusként, és a kód Windowson működik, majd LP64-en csendben egy érték felét olvassa. A feltételezett szélességek jelentése olyan sztringként, amelyet egy tesztben assertelni tud, egy egész osztálynyi platform ABI csúszást fordít egysoros ellenőrzéssé. Aki ugyanezzel a problémával küzdött meg a CK_ULONG kapcsán egy PKCS#11 kötésben, azonnal ráismer; az a történet a PKCS#11 struktúracsomagolás és a CK_ULONG szélessége cikkben olvasható
Miért üres tartalmat lát a második hitelesítési menet?
Mert a CMS_verify a detached tartalom BIO-ját fájl végéig olvassa, és egy egyszer kiolvasott BIO-t nem teker vissza Ön helyett. Két menetes hitelesítés ésszerű tervezés: először csak a kriptográfiai aláírás, a láncértékelés elnyomásával, aztán a teljes értékelés, és kivételesen megtévesztő módon bukik meg, ha mindkét menet ugyanazt a BIO-t használja
A második menet nulla bájtnyi tartalmat kap. Detached módban ez nem hiba, mert az üres tartalompuffer legális bemenet. A kivonat egyszerűen nem egyezik, és a hiba láncépítési hibaként tör felszínre, nem tartalmi hibaként, ami tanúsítványok és tanúsítványtárolók átvizsgálására küldi, miközben a valódi probléma egy streampozíció. Építse újjá minden menethez a memória BIO-t a BIO_new_mem_buf hívásával. Egy allokációba kerül, és a lehetőség teljes egészében megszűnik
Mit nyom el a no-verify flag, és mit nem
A CMS_NO_SIGNER_CERT_VERIFY a láncértékelést nyomja el, nem az aláíró tanúsítvány felkutatását. Belül az OpenSSL feloldja és csatolja az aláíró tanúsítványokat, mielőtt egyáltalán megnézné a flag-et, tehát az olyan első menet után, amely belevette ezt a flag-et, az aláíró már rendelkezésre áll, és az algoritmusazonosítói azonnal kiolvashatók. Nincs szükség második teljes hitelesítés futtatására pusztán az aláíró tanúsítvány megszerzéséért, mégis pontosan erre csábít a flag neve
Ehhez egy tulajdonosi szabály tartozik. Az aláíróhivatkozás a CMS struktúrához tartozik, és nem szabad önállóan felszabadítani. Addig érvényes, ameddig a struktúra, és a felszabadítása olyan sérülést produkál, amelynek tünete egészen máshol mutatkozik, általában egy közömbös objektum takarítása közben
Miért utasít el minden aláírást a CRL-ellenőrzés bekapcsolása?
Mert az OpenSSL a CRL-eket csak ahhoz képest ellenőrzi, amit a tár már tart, és semmit nem tölt le saját maga. Nem követi a CRL disztribúciós pontokat, és OCSP-t nem ismer. X509_V_FLAG_CRL_CHECK beállítása egy olyan táron, amelyben nincs CRL, minden láncot elbuktat tanúsítvány-CRL beszerzésének képtelenségével. Az eredmény úgy néz ki, mint egy visszavonás-ellenőrzés, amely működik és problémákat talál. Az valójában egy sosem lefutott visszavonás-ellenőrzés
A backend ezért csak akkor állítja be a flag-et, ha a ConfigureSslCrls ténylegesen szállított legalább egy CRL-t. Enélkül a RevocationStatus pcvsUnsupported-ként tér vissza, ami őszinte kijelentés arról, hogy a kérdés meg nem válaszolt maradt. Ugyanebből az okból az OnlineRetrieval-nak nincs hatása erre a backendre, és nem érkezik pcvstOnlineRetrieval checkpoint: nincs letöltési útvonal, amelyről a haladást jelenteni lehetne
Ez általánosságban is olyan tervezési álláspont, amelyet érdemes megvédeni. Egy validátor, amely nem tudja ellenőrizni a visszavonást, mondja is ki. Egy ellenőrizetlen tanúsítvány nem visszavontként jelentése a leggyakoribb módja annak, hogy az aláírásvalidáló eszközök félrevezetik a felhasználóikat, és pontosan az a zavarosztály, amelyet a miért utasítanak el a validátorok PAdES aláírásokat cikk jár körül
// A checkpointok megmutatják a felületnek, éppen mely szakasz fut, és
// megmondják, mely szakaszokat hajt végre valójában egy backend
type
TSignatureProbe = class
procedure Checkpoint(Stage: TPdfCmsVerifyStage);
end;
procedure TSignatureProbe.Checkpoint(Stage: TPdfCmsVerifyStage);
begin
case Stage of
pcvstCryptographicSignature: Status('checking the signature');
pcvstChainBuild: Status('building the certificate chain');
pcvstOnlineRetrieval: Status('fetching validation data');
pcvstRevocationCheck: Status('checking revocation');
end;
end;
// Olvassa a három verdiktet külön-külön; szabad, hogy ellentmondjanak
if Result.SignatureStatus = pcvsValid then
case Result.TrustStatus of
pcvsValid: Report('signed and trusted');
pcvsInvalid: Report('signed, chain rejected');
pcvsUnsupported,
pcvsIndeterminate: Report('signed, trust not established');
end;
if Result.RevocationStatus = pcvsUnsupported then
Report('revocation was not checked on this backend');
Kötés olyan könyvtárhoz, amelyet nem rögzíthet
Az OpenSSL az 1.0 és az 1.1 között átnevezte a stack-elérés függvényeit, tehát ugyanannak a logikai függvénynek két lehetséges exportneve van aszerint, hogy a gép éppen mely buildtel rendelkezik. A kötés előbb az újabb nevet oldja fel, és a régebbire esik vissza, és csak akkor jegyez fel hiányzó szimbólumot, ha egyik sem oldódik fel. Ez a helyes forma bármilyen dinamikus kötéshez olyan könyvtárhoz, amelyet Ön nem szállít: az aktuális neveket részesítse előnyben, tűrje a történelmieket, és csak a valódi hiányzást jelentse
A SslMissingSymbols az, ami egy megbukott betöltésből diagnosztizálható eseményt farag. Nem üres eredmény egy olyan gépen, amely nyilvánvalóan telepített libcrypto-val rendelkezik azt jelenti, hogy a telepített verzió régebbi, mint az az API, amelyet ez a build célz, ami teljesen más támogatási beszélgetés, mint egy hiányzó könyvtár. A ConfigureSslLibraryPath a másik gyakori esetet fedi le, a több OpenSSL buildtel rendelkező gépet, ahol az alapértelmezett keresési úton lévő nem az, amelyet Ön akar
Backend kiválasztása platformonként
A gyakorlati berendezkedés az, hogy induláskor választ, és rögzíti, melyik felelt. Windowson a platform backend integrálódik azokkal a tanúsítványtárolókkal, amelyeket a vállalat már kezel, ami normálisan az, amit Ön akar. macOS-en a Keychain backend ugyanarra az érvelésre illeszkedik, és a aláírások ellenőrzése SecTrust-tal macOS-en cikk írja le. Az OpenSSL a hordozható opció, és akkor is a helyes választás, amikor platformokon át azonos validációs politikára van szüksége, nem olyanra, amely minden platform tanúsítványtárolóját követi
Akármelyiket is telepíti, naplózza a PadesCmsVerificationBackendName-et minden rögzített verdikt mellé. Egy tárolt validációs eredmény a legyártó backend nélkül később nem reprodukálható, mert a három státuszérték finoman mást jelent aszerint, hogy melyik stack felelt. Mindezek fölötti aláírásvizsgálati réteget, beleértve a PAdES szintek jelentési módját is, a PDF digitális aláírások és PAdES szintek vizsgálata cikk tárgyalja
Mindez forrásként szállít a PDFium Delphi komponens részeként, ami itt a szokásosnál is többet számít: egy aláírásvalidátor számára az, hogy pontosan kiolvashassa, mely flag-eket állít be egy backend, és mely ellenőrzéseket hagyja ki, nem kellemes extra, hanem az egyetlen módja annak, hogy tudja, mit állít valójában a zöld pipa az Ön alkalmazásában