PDFium VCL voi nyt täydentää PDF-allekirjoitusketjun ja tarkistaa peruutuksen verkon yli OpenSSL-taustallaan: kun OnlineRetrieval on käytössä, ConfigureSslCmsVerifier asentaa varifoijan joka lataa puuttuvat välisertifiikatit AIA:n caIssuers-URLeista ja CRL:t CRL-jakelupisteistä, kiinteän ajan, pyyntö- ja tavubudjetin sisällä kutakin varifiointikutsua kohti. Ladatut sertifiikaatit ovat vain ja ainoastaan ketjumateriaalia. Luottamus tulee yhä yksinomaan järjestelmävarastosta ja ankkureista jotka konfiguroit
Kuilu jonka tämä sulkee näkyy ensimmäisellä kerralla kun varifoit oikean maailman PDF:eitä Linux-palvelimella. Suuri osa allekirjoittajista upottaa CMS:ään vain oman lehtisertifiikaattinsa, joten OpenSSL ei ulotu juureen, TrustStatus palautuu virheellisenä, ja peruutustarkistusta ei koskaan ajeta koska ketjusta ei koskaan tuli luotettavaa. Ennen v3.121.0:aa artikkelissa PDF-allekirjoitusten varifioinnista OpenSSL:llä PDFium VCL:ssä kuvattu OpenSSL-tausta oli ehdottoman offline ja OnlineRetrievalilla ei ollut vaikutusta siihen. Yksi asia kannattaa sanoa heti alkuun: PDFium-kone itse ei tee yhtään CMS-varifiointia, joten jokainen alla oleva sääntö asuu komponentin PAdES-kerroksessa ja sen OpenSSL-sidonnassa, missä voit lukea sen
Missä järjestyksessä OpenSSL-tausta varifoi, hakee ja tarkistaa?
Ensin eheys, sitten luottamus, sitten peruutus, ja verkkoa kosketaan vain niiden askelten välillä jotka sitä tarvitsevat. VerifyCmsWithSsl tarkistaa CMS-allekirjoituksen ja allekirjoitetut attribuutit (RFC 5652) ketjun evaluointi tukahdutettuna, ja jos se epäonnistuu, se palaa välittömästi ennen kuin hakuistunto edes on olemassa, joten rikki olevia tavuja kantava dokumentti ei laukaise yhtään lähtevää pyyntöä. Vain jos ketju sitten epäonnistuu ja OnlineRetrieval on päällä, se seuraa AIA-linkkejä ja varifoi uudelleen. CRL-jakelupisteet haetaan vasta kun ketju on luotettu, koska luottamattomaan polkuun roikkuva CRL ei todista mitään. Kolme tuomiota pysyvät erillään koko matkan: kelvollinen allekirjoitus puutteellisella ketjulla raportoidaan yhä kelvollisena allekirjoituksena
uses
PDFium, FPdfCrypto, FPdfCryptoSsl, FPdfPades;
var
Pdf: TPdf;
Probe: TPdfCmsVerifyOptions;
Diags: TPdfSslVerifyDiagnostics;
Trust: TPadesTrustValidationOptions;
Verdict: TPadesValidationResult;
I: Integer;
begin
ConfigureSslTrustAnchors(LoadCorporateRoots); // DER; ainoa ylimääräinen luottamus
ConfigureSslCmsVerifier;
Probe := TPdfCmsVerifyOptions.Default;
Probe.OnlineRetrieval := True;
Probe.CheckRevocation := True;
Diags := SslVerifyOptionsDiagnostics(Probe);
if psvdOnlineRetrievalIgnored in Diags then
Log('no HTTP transport or CMS_add1_cert: validation stays offline');
Trust := TPadesTrustValidationOptions.Default; // ptnpOffline oletuksena
Trust.NetworkPolicy := ptnpOnline;
Trust.CheckRevocation := True;
Trust.UrlRetrievalTimeoutMs := 10000; // kutakin varifiointikutsua kohti
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'signed-contract.pdf';
Pdf.Active := True;
Verdict := Pdf.ValidatePadesTrust(Trust);
for I := 0 to High(Verdict.Signatures) do
Log(Format('#%d trust=%d revocation=%d', [I,
Ord(Verdict.Signatures[I].CertificateTrustStatus),
Ord(Verdict.Signatures[I].RevocationStatus)]));
finally
Pdf.Free;
end;
end;
Miksi ladattuja sertifiikaatteja ei koskaan lisätä luottamusvarastoon?
Koska URLit tulevat validoitavasta sertifiikaatista, ja allekirjoittaja valitsi ne. authorityInfoAccessin caIssuers-merkintä (RFC 5280 §4.2.2.1) on vihje siitä missä myöntäjä asuu, ei mitään muuta. Jos mitä ikinä vastaa kyseiseen URLiin menisi luottamusankkurivarastoon, kuka tahansa voisi allekirjoittaa itsetehdylle avaimelle, osoittaa AIA:n omaan palvelimeensa ja saada vihreän tuomion. RetrieveIntermediates välittää siksi jokaisen jäsennetyn sertifiikaatin CMS_add1_certille, joka sijoittaa sen tämän yhden CMS-rakenteen luottamattomaan joukkoon, ja OpenSSL:n on yhä rakennettava polku siitä ankkuriin jonka konfiguroit tai jonka järjestelmävarasto jo kantaa. On olemassa myös hiljaisempi syy: CMS_verifyin sertifiikaattiargumentti ei ole pudotusvalmis korvike CMS:ään upotetuille sertifiikaateille, joten CMS:ään itsensä lisääminen on luotettava polku
Hakusilmukka on tarkoituksella kapea. RetrieveIntermediates ajaa enintään 4 kierrosta, joista jokainen kerää caIssuers-URLit jokaisesta nyt CMS:ssä olevasta sertifiikaatista, ja pysähtyy heti kun kierros ei lisää mitään tai aikabudjetti on kulunut. Vastauksen on jäsennyttävä d2i_X509illa yksittäiseksi DER-sertifiikaatiksi joka kuluttaa koko rungon; perässä roikkuvat tavut hylätään, ja PKCS#7 certs-only -nipun joka tarjoillaan .p7c-URLista ohitetaan sen sijaan että purettaisiin. Saman AIA-laajennuksen OCSP-hakutapa ohitetaan, koska kyseinen tausta ei puhu OCSP:tä. Peruutuspuolella RetrieveCrls lukee vain jokaisen DistributionPointin (RFC 5280 §4.2.1.13) fullName-URIt CMS-sertifiikaateista ja konfiguroiduista ankkureista, ja ladatut CRL:t menevät toiseen, itsenäiseen X509_STOREiin täyden ketjun CRL-tarkistuksella, joten puuttuva tai vanhentunut CRL muuttaa RevocationStatusia koskematta koskaan TrustStatusiin
// Tiivistetty funktiosta VerifyCmsWithSsl (FPdfCryptoSsl.pas); BIO:n pystytys jätetty pois.
// Jokainen _CMS_verify-kutsu saa tuoreen content-BIO:n
if _CMS_verify(Cms, nil, nil, Bio, nil,
CMS_NO_SIGNER_CERT_VERIFY or CMS_BINARY) <> 1 then
Exit; // rikki oleva allekirjoitus: ei verkkoa lainkaan
if Options.OnlineRetrieval and SslCapabilities.OnlineRetrieval then
FetchSession := TPdfCryptoFetchSession.Create(Options.UrlRetrievalTimeoutMs);
Store := BuildStore(False, nil, RevocationChecked, CrlsMalformed);
if (_CMS_verify(Cms, nil, Store, Bio, nil, CMS_BINARY) <> 1) and
(FetchSession <> nil) then
begin
RetrieveIntermediates(Cms, FetchSession); // CMS_add1_cert, vain luottamaton
// varifoi ketju uudelleen samaa ankkurivarastoa vasten
end;
if Options.CheckRevocation and (FetchSession <> nil) and
(Result.TrustStatus = pcvsValid) then
RetrievedCrls := RetrieveCrls(Cms, FetchSession);
// toinen, erillinen varasto: konfiguroidut CRL:t plus haetut
Store := BuildStore(True, RetrievedCrls, RevocationChecked, CrlsMalformed);
Mitä yksi varifiointikutsu maksaa enintään?
Kiinteä katto, jota yksi TPdfCryptoFetchSession valvoo ja jota yhden varifiointikutsun AIA- ja CRL-askleet jakavat. Rajat ovat vakioita tiedostossa FPdfCryptoHttp, ei ehdotuksia:
- Aika:
UrlRetrievalTimeoutMs, jonka oletus on 15000 sekäTPdfCmsVerifyOptions.Defaultissa ettäTPadesTrustValidationOptions.Defaultissa; 0:lla luotu istunto palautuu arvoon 30000, ja kello käynnistyy kun allekirjoitus on päässyt läpi, kattaen jokaisen myöhemmän pyynnön - Pyynnöt: enintään 8 istuntoa kohden, laskettuna ennen kuin kuljetusta yritetään, joten kuollut isäntäkin kuluttaa paikan
- Tavut: 1 MiB vastausta kohden ja 4 MiB yhteensä, ja yli 2048 merkin URLit hylätään ennen yhtään yhteyttä
Kirjanpito on tiukempi kuin ensisilmäykseltä näyttää. Epäonnistuneesta vastauksesta vastaanotetut tavutkin laskevat yhteensä vastaan, joten 404:llä suurella sivulla vastaava palvelin ei voi kuluttaa budjettia ilmaiseksi. Vastausrajaa ylittävä luku keskeyttää latauksen sen sijaan että välittäisi typistetyn rungon ASN.1-jäsentimelle, ja tyhjä runko kantava HTTP 200 hylätään suoraan, koska AIA-polku indeksoisi muuten tyhjän taulukon Data[0]in. Vain pelkät http://- ja https://-URLit menevät läpi, ilman uudelleenohjauksia, evästeitä, tunnistetietoja tai automaattista proxyn löytämistä, kun taas HTTPS pitää tavalliset sertifiikkaatti- ja isäntänimetarkistuksensa. URL-deduplikoinnin laajuus on yksi kutsu tahallaan: seuraavan validoinnin on pystyttävä näkemään tuoreelleen julkaistu CRL. Budjetti on myös kutsua kohden, ei dokumenttia kohden, ja ValidatePadesTrust varifoi jokaisen allekirjoituksen ja jokaisen aikaleimatokenin erikseen, joten pahin tapa kasvaa allekirjoitusten määrän mukaan
Miksi aikakatkaistu WinHTTP-pyyntö voi yhä kirjoittaa muistiisi?
Koska paluu aikakatkaisussa ei peruuta jo lennossa olevia callbackeja. Windows-kuljetus ajaa WinHTTP:tä asynkronisesti ja odottaa eventtiä istunnon jäljellä olevalla ajalla, ja kun kyseinen odotus luovuttaa, pyyntö voi yhä viimeistellä luvun ja signaloida sen jälkeen. Osoita asynkroninen luku pinopuskuriin ja kyseinen myöhäinen viimeistely kirjoittaa kehykseen joka kuuluu siihen mennessä jollekin ihan toiselle funktiolle. Korjaus on omistajuus, ei ajoitus: eventti ja 16 kilotavun lukupuskuri asuvat keossa olevassa tietueessa jolla on kaksi viittausta, toisen pitää soittaja ja toisen vapauttaa vain viimeinen HANDLE_CLOSING-callback, joten kumpi puoli viimeistelee viimeisenä vapauttaa muistin
type
PHttpState = ^THttpState;
THttpState = record
References: LongInt; // soittaja + viimeinen HANDLE_CLOSING-callback
Event: THandle;
Status, Count: DWORD;
Buffer: array[0..16383] of Byte; // asynkroniset luvut laskeutuvat tähän, ei koskaan pinoon
end;
procedure ReleaseState(State: PHttpState);
begin
if InterlockedDecrement(State.References) = 0 then
begin
CloseHandle(State.Event);
Dispose(State);
end;
end;
// Status-callbackissa: HANDLE_CLOSING on viimeinen ilmoitus jonka WinHTTP
// lähettää pyynnölle, joten se pudottaa toisen viittauksen
if Status = HttpHandleClosing then
begin
ReleaseState(State);
Exit;
end;
Mitä libcurlin on tarjottava FPC Unixilla
Asynkroninen resolveri ja säieturvallinen buildi, tai verkkohaku pysyy pois. FPC Unixilla kuljetus kulkee libcurlin kautta, sama riippuvuus jonka takana on libcurl-aikaleimatausta ei-Windows-kohteille, ja sidonta hylkää minkä tahansa kirjaston jonka feature-maskilta puuttuu joko CURL_VERSION_ASYNCHDNS tai CURL_VERSION_THREADSAFE. Syy on se, että CURLOPT_NOSIGNAL, jonka toisen prosessin sisällä elävän kirjaston on asetettava, yhdistettynä synkroniseen resolveriin tarkoittaa että DNS-haku voi yksinkertaisesti elää aikakatkaisun yli. Toinen ansa on sammutus: curl_global_cleanup ei odota asynkronisia DNS-säikeitä, joten kun libcurl on kerran alustettu, moduuli pysyy mmapattuna prosessin päättymiseen asti sen sijaan että taustasäie pääsisi juoksemaan purettuun koodiin. Kun kumpikaan vaatimus epäonnistuu, SslCapabilities.OnlineRetrieval on False ja SslVerifyOptionsDiagnostics raportoi psvdOnlineRetrievalIgnoredin sen sijaan että esittäisi verkkoon oltiin yhteydessä
Mitä tulos takaa ja mitä ei
Kelvollinen RevocationStatus kyseiseltä taustalta tarkoittaa että koko ketjun kattavia ajantasaisia CRL:eitä löydettiin, konfiguroitiin tai ladattiin, eikä yksikään listannut siinä olevaa sertifiikaattia; ei mitään muuta. OCSP:tä ei ole, joten CA joka julkaisee peruutukset vain OCSP:n kautta jättää tuloksen ei-tuetuksi, ja verkkovirhe näyttää täsmälleen samalta kuin CA joka ei julkaise mitään. Huomaa myös että psvdNoCrlsConfigured kuvaa vain konfiguroimiasi CRL:eitä, joten verkkohakua käytettäessä se on vihje, ei ennuste epäonnistumisesta. Kun auditointijäljen on oltava toistettavissa ilman verkkoyhteyttä, jätä NetworkPolicy ptnpOffline-oletukseensa: hakuistuntoa ei luoda ja tausta ei koskaan avaa yhteyttä, mikä vastaa CryptoAPI-puolen offline-sopimusta jota käsittelee artikkeli offline-PDF-allekirjoitusten peruutustarkistuksista Windowsissa
Hakukoodi, budjetit ja kuljetussidonnat toimitetaan lähdekoodina mukana PDFium Delphi -komponentin, joten voit varmistaa täsmälleen mitkä URLit validointi saa kontaktoida ja kuinka paljon se saa ladata ennen kuin otat ptnpOnlinein käyttöön palvelimella joka käsittelee luottamattomia dokumentteja