PDFium VCL kohtelee CMS-varmennusta vaihdettavana taustajärjestelmänä IPdfCmsVerifier-rajapinnan takana, joten PAdES-validaattori voi ajaa Windowsilla CryptoAPI:n kautta, macOS:lla Keychainin kautta ja missä tahansa, missä OpenSSL on läsnä, ConfigureSslCmsVerifierin kautta. Rajapinta on pieni. Kolme OpenSSL-käytöstä sen alla tuottaa itsevarmoja, vääriä vastauksia, jos toteutat sen naiivisti
Motivaatio on riittävän selvä heti, kun Delphi-sovellus lähtee Windowsista. Allekirjoituksen validointi on yksi niistä harvoista alueista, joissa alustan crypto-pino ei ole toteutuksen yksityiskohta: se päättää, mitkä varmenteet ovat luotettuja, mitkä algoritmit ovat olemassa ja mitä peruutus tarkoittaa. Kovakoodaa yksi, eikä koodi porttaudu. Abstraktoi se huonosti, ja jokainen alusta raportoi erimuotoisen vastauksen, jota kutsuja ei voi vertailla
Mitä abstraktion oikeasti on kannettava
Kaksi varmennuksen muotoa ja kolme itsenäistä tuomiota. PDF-allekirjoitus on irrotettu (detached): allekirjoitettu sisältö on kaksi tavualuetta /Contents-aukon kummallakin puolella, joten VerifyDetached ottaa kaksi segmenttiä yhden puskurin sijaan. Aikaleimatunnus on kiinnitetty (attached), kantaen omaa sisältöään, joten VerifyAttached ottaa vain DERin
Tulos jakautuu kolmeen tilaan, koska ne vastaavat kolmeen eri kysymykseen ja voivat eriä. SignatureStatus sanoo, allekirjoittivatko tavut allekirjoittajan varmenteen avain. TrustStatus sanoo, ketjuuntuuko kyseinen varmenne johonkin, johon luotat. RevocationStatus sanoo, oliko varmenne yhä voimassa relevanttina hetkenä. Dokumentti, jossa on matemaattisesti täydellinen allekirjoitus varmenteesta, josta et ole koskaan kuullut, on voimassa, epäluotettava ja tuntematon, ja sen kutistaminen yhdeksi booleaniksi on tapa, jolla validaattorit päätyvät valehtelemaan käyttäjille
uses
FPdfCrypto, FPdfCryptoSsl;
var
Options: TPdfCmsVerifyOptions;
begin
if not SslAvailable then
raise Exception.Create('libcrypto not usable: ' + SslMissingSymbols);
ConfigureSslTrustAnchors(LoadCorporateRoots); // DER, voi olla tyhjä
ConfigureSslCrls(LoadFreshCrls); // DER, voi olla tyhjä
ConfigureSslCmsVerifier; // asentaa taustan
Writeln('backend : ', PadesCmsVerificationBackendName);
Writeln('library : ', SslLibraryPath, ' ', SslLibraryVersion);
Writeln('ABI : ', SslAbiLayout); // ulong=<n> long=<n>
Options := TPdfCmsVerifyOptions.Default;
Options.CheckRevocation := True;
Options.CollectChainCertificates := True;
end;
SslAbiLayout näyttää kuriositeetiltä eikä ole. Jokainen OpenSSL-virhekoodi ja jokainen store-lippu ylittää rajan C:n unsigned longina, joka on neljä tavua Windowsilla ja kahdeksan Linuxilla ja macOS:lla. Määrittele se kiinteänä 32-bittisenä tyyppinä, ja koodi toimii Windowsilla ja lukee sitten hiljaa puolet arvosta LP64:llä. Oletettujen leveyksien raportoiminen merkkijonona, johon voi väittää testissä, muuntaa koko alusta-ABI-ajautumisen luokan yhden rivin tarkistukseksi. Jokainen, joka on pohtinut samaa ongelmaa CK_ULONGin kanssa PKCS#11-sidonnassa, tunnistaa sen heti; tuo tarina on artikkelissa PKCS#11-rakenteiden pakkaus ja CK_ULONGin leveys
Miksi toinen varmennuskierros näkee tyhjää sisältöä?
Koska CMS_verify lukee irrotetun sisällön BIOn tiedoston loppuun, ja luettua BIOta ei kelata puolestasi. Varmennus kahdella kierroksella on järkevä suunnittelu: ensin pelkkä kryptografinen allekirjoitus ketjuarvioinnin ollessa tukahdutettuna, sitten täysi arviointi, ja se epäonnistuu epätavallisen harhaanjohtavalla tavalla, jos molemmat kierrokset jakavat yhden BIOn
Toinen kierros saa nolla tavua sisältöä. Detached-tilassa se ei ole virhe, koska tyhjä sisältöpuskuri on laillinen syöte. Tiivistelmä ei yksinkertaisesti täsmää, ja vika ilmestyy ketjunrakennusvikana sisältövikana sijaan, mikä lähettää sinut tutkimaan varmenteita ja trust storeja, kun todellinen ongelma on virran sijainti. Rakenna muisti-BIO uudelleen BIO_new_mem_bufilla joka kierrokselle. Se maksaa yhden varauksen ja poistaa mahdollisuuden kokonaan
Mitä no-verify-lippu tukahduttaa ja mitä ei
CMS_NO_SIGNER_CERT_VERIFY tukahduttaa ketjuarvioinnin, ei allekirjoittajan varmenteen hakua. Sisäisesti OpenSSL ratkaisee ja kiinnittää allekirjoittajan varmenteet ennen kuin se kuulee lippua, joten ensimmäisen lipun kantaneen kierroksen jälkeen allekirjoittaja on jo saatavilla ja sen algoritmitunnisteet voi lukea suoraan. Ei ole tarvetta ajaa toista täyttä varmennusta vain saadakseen allekirjoittajan varmenteen, mikä on se, mihin lipun nimi houkuttelee sinua olettamaan
Yksi omistussääntö kuuluu tuon mukana. Allekirjoittajan viittaus kuuluu CMS-rakenteelle, eikä sitä saa vapauttaa itsenäisesti. Se on voimassa niin kauan kuin rakenne, ja sen vapauttaminen tuottaa korruption, jonka oire ilmestyy aivan muualla, yleensä asiaattoman objektin siivouksen aikana
Miksi CRL-tarkistuksen kytkeminen hylkää jokaisen allekirjoituksen?
Koska OpenSSL tarkistaa CRL:t vain sitä vasten, mitä store jo pitää, eikä nouda mitään omin voimin. Se ei seuraa CRL-jakelupisteitä eikä se puhu OCSP:tä. Aseta X509_V_FLAG_CRL_CHECK storeen, jossa ei ole CRL:ejä, ja jokainen ketju kaatuu kyvyttömyyteen saada varmenteen CRL:tä. Tulos näyttää peruutustarkistukselta, joka toimii ja löytää ongelmia. Kyseessä on peruutustarkistus, joka ei koskaan aja lainkaan
Tausta asettaa siksi lipun vain, kun ConfigureSslCrls on oikeasti toimittanut vähintään yhden CRL:n. Ilman yhtään RevocationStatus palaa pcvsUnsupportedina, mikä on rehellinen lausunto siitä, että kysymykseen ei vastattu. Samasta syystä OnlineRetrievalilla ei ole vaikutusta tähän taustaan eikä pcvstOnlineRetrieval-tarkistuspistettä emitoida: ei ole noutopolkua, josta raportoida edistymistä
Tämä on suunnittelun asema, joka kannattaa puolustaa yleisesti. Validaattori, joka ei voi tarkistaa peruutusta, pitäisi sanoa sen. Tarkistamattoman varmenteen raportoiminen ei-peruutetuksi on yleisin yksittäinen tapa, jolla allekirjoituksen validointityökalut harhaanjohtavat käyttäjiään, ja kyseessä on täsmälleen se sekaannusten luokka, jota artikkeli miksi validaattorit hylkäävät PAdES-allekirjoitukset tutkii
// Tarkistuspisteet antavat käyttöliittymän näyttää, mikä vaihe on käynnissä,
// ja kertovat, mitkä vaiheet tausta oikeasti suorittaa
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;
// Lue kolme tuomiota erikseen; niiden sallitaan eriävän
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');
Sitoutuminen kirjastoon, jota et voi pinnata
OpenSSL nimesi pinon hakufunktionsa uudelleen 1.0:n ja 1.1:n välillä, joten samalla loogisella funktiolla on kaksi mahdollista vientinimeä riippuen siitä, kumman käännöksen isäntä sattuu omistamaan. Sidonta ratkaisee uudemman nimen ensin ja palaa vanhempaan, ja kirjaa puuttuvan symbolin vasta, kun kumpikaan ei ratkea. Tuo on oikea muoto mille tahansa dynaamiselle sidonnalle kirjastoon, jota et toimita: suosi nykyisiä nimiä, siedä historiallisia ja raportoi vain aito puute
SslMissingSymbols on se, mikä muuntaa epäonnistuneen latauksen diagnosoitavaksi tapahtumaksi. Ei-tyhjä tulos isännällä, jolla on selvästi libcrypto asennettuna, tarkoittaa, että asennettu versio on vanhempi kuin API, jota tämä käännös kohdentaa, mikä on täysin eri tukikeskustelu kuin puuttuva kirjasto. ConfigureSslLibraryPath kattaa toisen yleisen tapauksen, isännän, jolla on useita OpenSSL-käännöksiä ja jossa oletushakupolulla oleva ei ole se, jota haluat
Taustan valinta per alusta
Käytännön järjestely on valita käynnistyksessä ja kirjata, kumpi vastasi. Windowsilla alustatausta integroituu varmenestoreihin, joita yritys jo hallinnoi, mikä on yleensä se, mitä haluat. macOS:lla Keychain-tausta sopii samaan päättelyyn ja se on kuvattu artikkelissa allekirjoitusten varmennus SecTrustilla macOS:lla. OpenSSL on siirrettävä vaihtoehto, ja se on myös oikea valinta, kun tarvitset validointipolitiikan, joka on identtinen alustoilla eikä sellainen, joka seuraa kunkin alustan trust storea
Kumman tahansa asennat, kirjaa PadesCmsVerificationBackendName jokaisen tallentamasi tuomion viereen. Tallennettu validointitulos ilman taustaa, joka sen tuotti, ei ole myöhemmin toistettavissa, koska kolmella tila-arvolla on hienovaraisesti eri merkitykset riippuen siitä, kumpi pino vastasi. Allekirjoitusten tarkastuskerros kaiken tämän päällä, mukaan lukien se, miten PAdES-tasot raportoidaan, on käsitelty artikkelissa PDF:n digitaalisten allekirjoitusten ja PAdES-tasojen tarkastus
Kaikki tämä toimituu lähteinä PDFium Delphi -komponentin mukana, mikä merkitsee täällä enemmän kuin yleensä: allekirjoituksen validaattorille kyky lukea tarkasti, mitkä liput tausta asettaa ja mitkä tarkistukset se ohittaa, ei ole mukavuuslisä, vaan ainoa tapa tietää, mitä vihreä valintamerkki sovelluksessasi oikeasti väittää