PDFium VCL allekirjoittaa PAdES-dokumentit macOS Keychainissa pidetyllä yksityisellä avaimella taustajärjestelmän kautta, joka ratkaisee jokaisen Security- ja CoreFoundation-symbolin ajonaikana dlopenilla ja dlsymillä. Mikään ei ole linkitysaikana sidottua, mikä tarkoittaa, että väärin kirjoitettu symbolinimi ilmestyy KeychainAvailablein palauttamana Falsena ja KeychainMissingSymbolsin nimeämänä syyllisenä, linkitysvirheen tai kaatumisen sijaan
Tuon valinnan pakotti epämukava rajoite, ja sen käsittelytapa yleistyy. Yksikkö kirjoitettiin koneella, jossa ei ollut macOS SDK:ta, joten jokainen frameworkin symbolinimi ja jokainen vakio tuli dokumentaatiosta, eikä mitään niistä voinut tarkistaa otsikkotiedostoa vasten. Väärä vastaus siihen tilanteeseen on kirjoittaa koodi huolellisesti ja toivoa. Oikea on järjestää niin, että väistämättömät virheet ilmoittavat itsestään mahdollisimman paikannettavassa muodossa
Miksi dynaaminen sidonta on oikea valinta jopa kohdealustalla
Koska se muuntaa vikaluokan, joka pysäyttää ohjelman, vikaluokaksi, joka raportoi itsestään. Staattisesti linkitetty framework-viittaus, joka on väärä, epäonnistuu linkitysaikana kohteella eikä koskaan linkity muualla. Dynaamisesti sidottu, joka on väärä, tuottaa käytettävissä olemattoman taustan ja luettelon ratkaisemattomista nimistä, ja ensimmäinen ajo Macilla kääntää kysymyksen miksi tämä ei ole käytettävissä yhdeksi riviksi, joka nimeää kirjoitusvirheen
On toinenkin hyöty, joka maksaa itsensä takaisin päivittäin eikä kerran. Koska yksikkö ei linkitä frameworkkeja, se kääntyy jokaisella alustalla, joten tavallinen Windows-käännös pitää tarkistamassa sen syntaksia, tyyppejään ja uses-lausettaan. Yksikkö, joka kääntyy vain alustalla, jota kukaan tiimissä ei omista, on yksikkö ilman mitään kääntäjää katsomassa sitä, ja se rappeutuu hiljaa jokaisen jaetun tyypin refaktorin myötä
uses
FPdfCrypto, FPdfCryptoMac;
var
Options: TPadesSignerOptions;
begin
if not KeychainAvailable then
raise Exception.Create('Keychain backend unavailable, unresolved: ' +
KeychainMissingSymbols);
ConfigureKeychainSignerProvider; // asenna PAdES-allekirjoitustaustaksi
ConfigureKeychainCmsVerifier; // ja varmennustaustaksi
Writeln('signer backend : ', PadesCryptoBackendName);
Writeln('verify backend : ', PadesCmsVerificationBackendName);
Options := TPadesSignerOptions.Default;
Options.CertificateThumbprint := 'B1 3F 9C ...'; // SHA-1, mikä kirjainkoko tahansa
Options.PaddingScheme := psRsaPss;
end;
Kahdenlaista vietyä symbolia, kaksi tapaa lukea niitä
Tämä on koko sidonnan sekavin yksittäinen yksityiskohta, ja sen saaminen väärin päin kääntyy puhtaasti ja epäonnistuu ajonaikana. CoreFoundation ja Security vievät kaksi kategorisesti erilaista asiaa saman dlsym-kutsun kautta, ja koodin on tunnettava, kumpi on kumpi
Nimetyt vakiot, kuten keychain-alkuluokan avaimet ja CoreFoundationin boolen singletonit, ovat viettyjä muuttujia, joiden sisältö on haluamasi CFStringRef tai CFBooleanRef. dlsym palauttaa kyseisen muuttujan osoitteen, joten sinun on dereferoitava kerran saadaksesi arvon. Callback-taulurakenteet, kuten sanakirjan avain- ja arvocallbackit, ovat viettyjä rakenteita, ja dlsym palauttaa rakenteen osoitteen, mikä on täsmälleen se osoitin, jota sanakirjan luontifunktio odottaa. Dereferoi tuo, ja passaat rakenteen ensimmäisen konesanan ikään kuin se olisi osoitin
Kumpikaan virhe ei tuota käännösvirhettä, eikä kumpikaan tuota selkeää ajonaikaista virhettä. Saat roskaposittimen, joka epäonnistuu jossain alavirtassa. Tapa verottaa erottelu mahdottomaksi saada väärin on lopettaa sen muistamiseen nojaaminen: kaksi apufunktiota, toinen, joka sitoo ja dereferoi, ja toinen, joka sitoo muttei, joten kutsupaikka julistaa, minkälaista symbolia se pyytää, ja apuri valvoo loput
// Viety muuttuja: dlsym antaa muuttujan osoitteen, joka pitää
// CFTypeRefiä, joten dereferoi kerran
FSecClassKey := BindConstant(SecurityLib, 'kSecClass');
// Viety rakenne: dlsym antaa rakenteen OSOITTEEN, mikä on se,
// mitä API haluaa. Älä dereferoi
FKeyCallbacks := BindStruct(CoreFoundationLib,
'kCFTypeDictionaryKeyCallBacks');
Miksi RSA-PSS-allekirjoitus tarvitsee kaksi erillistä varapolkua?
Koska algoritmi voi puuttua kahdella itsenäisellä tavalla, ja vain yksi niistä on versiokysymys. PSS-tiivistysallekirjoitusalgoritmin vakio ilmestyi macOS 10.13:ssa, joten vanhemmalla järjestelmällä symboli yksinkertaisesti ei ole siellä ja sidonta saa nilin. Tuo on versiotarkistus. Erikseen järjestelmällä, jossa vakio on olemassa, tietty avain voi silti kieltäytyä siitä, ja framework vastaa tuon kysymyksen SecKeyIsAlgorithmSupportedilla kyseiselle avaimelle. Laitteistopohjainen avain tai rajoittavilla attribuuteilla varustettu avain voi kieltäytyä PSS:stä, kun taas ohjelmistoavain samalla koneella hyväksyy sen
Molempien polkujen on johdettava samaan varapolkuun: vaihda PKCS#1 v1.5:een. Ja kriittinen osa on, että varapolun on muutettava myös CMS-rakenteeseen kirjoitettava algoritmitunniste, ei vain allekirjoituskutsua. PSS-algoritmitunnisteen emittoiminen tuottaen oikeasti v1.5-allekirjoitusta tuottaa dokumentin, jonka jokainen verifiointi hylkää täysin, mikä on ankarasti pahempaa kuin raportti, ettei PSS ole tuettu. Alennus on hyväksyttävä, ristiriita sen välillä, mitä julistat ja mitä teit, ei ole, ja se on yleinen sääntö allekirjoituskoodille eikä macOS:n erikoisuus. Allekirjoitustason vaikutukset on ladottu artikkelissa PDF-tiedostojen allekirjoittaminen PAdES B-B:llä
ECDSA-allekirjoituksen koodaus ja käänteisyys, joka kannattaa huomioida
Elliptisen käyrän polku ei tarvitse lainkaan muunnosta macOS:lla, ja se on päinvastainen sille, mitä PKCS#11-sidonta vaatii. Security frameworkin tiivistysallekirjoitusalgoritmi ECDSA:lle palauttaa allekirjoituksen jo X9.62 DER-muodossa, mikä on täsmälleen se, mitä CMS haluaa. PKCS#11-tunnus palauttaa sen sijaan raakan kiinteäleveän P1363-parin, joka on koodattava uudelleen ennen kuin se menee allekirjoitusrakenteeseen
Joten kaksi taustaa, jotka toteuttavat saman rajapinnan, tarvitsevat vastakkaisen kohtelun samalle algoritmille, eikä kumpikaan ole väärässä. Tämä on täsmälleen se erojen laji, jonka abstraktion on nieltävä paljastamisen sijaan: PAdES-kerros pyytää palveluntarjoajaa allekirjoittamaan, ja koodauskonventiot pysyvät palveluntarjoajan sisällä. Jos ne vuotavat ylöspäin, jokainen kutsuja päätyy kantamaan per tausta -ehdollistusta. Sama muoto ilmestyy artikkelin etä-PAdES-allekirjoitusistunnot HSM:ää vasten kuvailemassa etäallekirjoitustarinassa
// Palveluntarjoajan rajapinta on sama jokaisella alustalla, joten valinta on
// käynnistyspäätös eikä per kutsu tehtävä päätös
{$IFDEF DARWIN}
if KeychainAvailable then
ConfigureKeychainSignerProvider;
{$ENDIF}
{$IFDEF MSWINDOWS}
// Windowsin CNG-palveluntarjoaja asennetaan alustayksikössä
{$ENDIF}
if not PadesCryptoAvailable then
raise Exception.Create('no signing backend on this platform');
// Tästä eteenpäin allekirjoituskoodi on alustaneutraalia
Signer := ResolvePadesSigner(Options);
Referenssilaskennan säännöt, jotka istuvat kolmen rivin päässä
Core Foundationin muistinhallinta noudattaa nimikäytäntöjä, ja ansa täällä on, että eri konventioiden funktiot ilmestyvät vierekkäin samassa lyhyessä lohkossa. Funktio, joka hakee varmenteen trust-objektista, palauttaa lainatun viittauksen, jota ei saa vapauttaa. Funktiot, jotka kopioivat allekirjoittajan varmenteen tai kopioivat sen datan, palauttavat omistetut viittaukset, jotka on vapautettava. Kolme kutsua peräkkäin, kaksi omistussääntöä, ja lainatun vapauttaminen ei kaadu tuolla rivillä. Se korruptoi retain-laskurin ja kaataa jotain asiaatonta myöhemmin
Liuvutus on lukea verbi jokaisesta frameworkfunktion nimestä ennen siivouksen kirjoittamista, joka kerta, ilman poikkeusta. Se on CoreFoundationin vastine sille, tarkistaako API palauttaako kopion vai näkymän, ja väärin saamisen hinta on ajoittainen kaatuma eikä virhe
Mitä tämä tausta ei väitä
Se ei ole koskaan ajanut macOS:lla kirjoitushetkellä, ja sen sanominen suoraan on hyödyllisempää kuin vihjattu takuu. Se, mikä on todistettavasti totta, on kapeampaa ja yhä arvokasta: yksikkö kääntyy Windowsilla osana päivittäistä käännöstä, jokainen framework-symboli sidotaan nimellä ajonaikana viat lueteltuina, ja algoritmin valintalogiikka molempine PSS-varapolkuineen on tavallista Pascalia, jota voi katselmoida ja josta voi päästellä. Ensimmäinen ajo Macilla joko toimii tai tuottaa luettelon korjattavista nimistä
Varmennusvastine, joka käyttää korkeamman tason CMS-dekooderia CMS-rakenteen käsin kokoamisen sijaan, on käsitelty artikkelissa PDF-allekirjoitusten varmennus macOS:lla SecTrustilla, ja se jakaa saman sidontainfrastruktuurin ja saman diagnostiikkalähestymistavan
Siirrettävä idea tässä koskee riskin sijoittelua eikä macOS:ia. Kun sinun on kirjoitettava koodia rajapintaa vasten, jota et voi varmistaa, valitse rakennelma, jossa virheet ovat halvimpia paikallistaa. Dynaaminen sidonta eksplisiittisellä luettelolla ratkaisemattomista nimistä muuntaa kaksikymmentä varmistamatonta oletusta yhdeksi diagnostiikkariviksi. Molemmat taustat toimituvat lähteinä PDFium Delphi -komponentin mukana, joten jos symbolinimi kaipaakin korjausta, se on yhden rivin muutos omassa puussasi eikä tukipyyntö