PDF-allekirjoitus on enimmäkseen tavukirjanpitoa, ja tavukirjanpidossa se menee pieleen. Salaus toimii koodilla, joka on auditoitu kahden vuosikymmenen ajan, ja tuo osa ei melkein koskaan epäonnistu. Tuotannossa epäonnistuu nöyrempi asia: varsinaiselle allekirjoitukselle liian pieneksi varattu paikanvaraaja, väärän tiedoston venytyksen yli otettu tiiviste tai allekirjoituksen jälkeinen "tallennus", joka kirjoitti hiljaa uusiksi tavut, jotka allekirjoitus oli jo jäädyttänyt. Aseta tavut oikein ja vihreä valintamerkki huolehtii itsestään
HotPDF kattaa allekirjoituksen Delphille ja C++Builderille kolmella tasolla, ja valitset niiden välillä vastaamalla yhteen kysymykseen: missä yksityinen avain asuu? PFX-tiedosto levyllä tarvitsee yhden funktiokutsun. HSM:ään tai etäallekirjoituspalveluun lukittu avain tarvitsee varaa-tiivistä-lisää-sekvenssin, koska mikään kirjasto ei voi kurkottaa tunnisteeseen ja vetää avainta ulos. Allekirjoitus, jonka on täytettävä eurooppalainen sääntely, tarvitsee PAdES-perusrakenteet sen lisäksi. Alla olevat kohdat noudattavat tuota etenemistä
Kuinka /ByteRange kiinnittää allekirjoitetut tavut alas
Allekirjoituksen täytyy asua allekirjoittamansa tiedoston sisällä, eikä se voi allekirjoittaa itseään. PDF kiertää paradoksin jättämällä aukon. Ennen allekirjoittamista kirjoittaja varaa kiinteän kokoisen /Contents-merkinnän täynnä nollia ja tallentaa /ByteRange-taulukon kahta jännettä varten sen molemmin puolin: kaiken ennen aukkoa, kaiken sen jälkeen. Allekirjoittaja tiivistää nämä kaksi jänneväliä ja kirjoittaa syntyvän CMS-möykyn aukkoon heksadesimaalina. Ansa on sanassa kiinteä. Sitoudut kyseisen aukon kokoon ennen kuin tiedät, kuinka suuri valmiista allekirjoituksesta tulee, joten varauksen on oltava luottavainen yliarvio. Kahdeksan kilotavua sisältää mukavasti irrotetun CMS-allekirjoituksen, jossa on lyhyt varmenneketju
HotPDF jakaa nämä kaksi tapausta kahteen kutsuun, ja niiden sekoittaminen on yleinen varhainen virhe. AddSignatureField pudottaa tyhjän, näkyvän kentän, jonka henkilö voi myöhemmin allekirjoittaa katseluohjelmassa. AddSignedSignatureField luo kentän ja varaa /Contents-aukon, joka on se, jonka haluat aina, kun koodi, ei ihminen, saattaa allekirjoituksen loppuun. Ojenna ulkoiselle allekirjoittajalle tyhjä kenttä ja sillä ei ole mitään täytettävää
Yhden kutsun polku: allekirjoittaminen PFX:stä
Kun varmenne ja sen yksityinen avain sijaitsevat PFX/PKCS#12-tiedostossa, jonka prosessisi voi lukea, koko putki pienenee luokkafunktioksi:
if THotPDF.SignPDFWithPFX('invoice-unsigned.pdf', 'invoice-signed.pdf',
'company-cert.pfx', 'pfx-password') then
Writeln('Signed: invoice-signed.pdf')
else
raise Exception.Create('PFX signing failed');
Kun tämä epäonnistuu, PDF on harvoin ongelma. PFX on. HotPDF lukee PBES2:lla suojatut kontit, mikä tarkoittaa PBKDF2-avaimen johtamista AES-256-CBC:n kautta. Vanhemman Windows-varmenteiden ohjatun toiminnon tai ennen 3.0:aa OpenSSL:n viemä PFX on yleensä kiedottu sen sijaan vanhaan RC2:een tai 3DES:ään, ja se yksinkertaisesti ei jäsenny. Korjauksena on viedä kontti uudelleen kerran modernilla suojauksella; nykyinen OpenSSL tekee tämän oletuksena, eikä se ole koodimuutos. Joten kun allekirjoitus kuolee välittömästi varmenteessa, joka "toimii kaikkialla muualla", katso, kuinka PFX on luotu, ennen kuin epäilet omaa koodiasi
Varaa-tiivistä-lisää -polku HSM:ille ja tunnisteille
Yhden puhelun polku olettaa, että prosessisi voi lukea avaimen tiedostona. Yhä useammin se ei voi. Avain istuu HSM:ssä, USB-tunnisteessa tai allekirjoituspalvelun API:n takana, eikä kirjastolla ole mitään keinoa tavoittaa sitä suoraan. HotPDF käsittelee sen murtamalla allekirjoituksen tavutason vaiheisiin: kirjoita paikkamerkkiasiakirja, pyydä kirjastolta tiivistealueet, siirrä tiivistesyöte sille, mikä avainta pitää, ja liitä sitten palautettu CMS takaisin reikään
var
Doc: THotPDF;
Fs: TFileStream;
PdfBytes, HashInput, SigHex: AnsiString;
R1Start, R1Len, R2Start, R2Len, CStart, CLen: Integer;
begin
// 1. Kirjoita asiakirja varatulla /Contents -reiällä
Doc := THotPDF.Create(nil);
try
Doc.FileName := 'placeholder.pdf';
Doc.BeginDoc;
Doc.CurrentPage.AddSignedSignatureField('Sig1',
Rect(50, 100, 350, 150), 8192, 'adbe.pkcs7.detached',
'Contract approval', 'Boston, MA', 'legal@example.com');
Doc.EndDoc;
finally
Doc.Free;
end;
// 2. Lataa tallennetut tavut; palautetut poikkeamat ovat 0-pohjaisia
Fs := TFileStream.Create('placeholder.pdf', fmOpenRead);
try
SetLength(PdfBytes, Fs.Size);
Fs.ReadBuffer(PdfBytes[1], Fs.Size);
finally
Fs.Free;
end;
THotPDF.PreparePDFForSigning(PdfBytes, R1Start, R1Len, R2Start, R2Len,
CStart, CLen);
// 3. Tiivistä molemmat jänteet ja allekirjoita ulkoisesti (HSM, tunniste, palvelu)
HashInput := Copy(PdfBytes, R1Start + 1, R1Len) +
Copy(PdfBytes, R2Start + 1, R2Len);
SigHex := SignWithHsm(HashInput); // integraatiosi: palauttaa CMS:n heksana
// 4. Liitä allekirjoitus varattuun reikään
THotPDF.InsertSignatureHex(PdfBytes, SigHex);
Fs := TFileStream.Create('signed.pdf', fmCreate);
try
Fs.WriteBuffer(PdfBytes[1], Length(PdfBytes));
finally
Fs.Free;
end;
end;
Kaksi yksityiskohtaa tässä sekvenssissä aiheuttavat suurimman osan ajoittaisista epäonnistumisista. Ensimmäinen on, että PreparePDFForSigning toimii valmiin tiedoston tavuilla. Paikkamerkki on kirjoitettava ja tallennettava kokonaisuudessaan, ennen kuin siirroilla on mitään merkitystä; laske ne virtaa vasten, jota vielä kootaan, ja ne eivät asetu riviin tavujen kanssa, jotka lopulta tiivistät. Toinen on jälleen varauskoko. Pyytämäsi 8192 tavua on pidettävä sisällään lopullinen CMS, ja välivarmenteita kuljettava allekirjoitus tai palvelun allekirjoitetuilla attribuuteilla koristelema allekirjoitus voi mennä sen ohi. InsertSignatureHex ei kasvata reikää tehdäkseen tilaa. Ilmentymä on putkilinja, joka allekirjoittaa hienosti yhdellä varmenteella ja epäonnistuu seuraavalla; lääke on luoda paikkamerkki uudelleen varauksella, joka on mitattu todellisen allekirjoittajan tuottamasta todellisesta allekirjoituksesta, ei arvatusta
PAdES-peruslinjat ja aikaleimat, jotka pitävät allekirjoituksen elossa
Jos allekirjoitat eurooppalaisten sääntöjen mukaisesti, pelattavana oleva standardi on ETSI EN 319 142-1, joka pinoaa neljä PAdES-perustasoa. B-B on tavallinen allekirjoitus. B-T lisää luotetun aikaleiman, joka todistaa sen tekohetken. B-LT upottaa vahvistusmateriaalin, varmenteet ja peruuttamistiedot asiakirjan sisään, jotta se voidaan vielä tarkistaa vuosia myöhemmin. B-LTA asettaa säännölliset asiakirjojen aikaleimat päälle, joten todisteet ylittävät algoritmit, joiden varaan se on rakennettu. HotPDF lähettää asiakirjapuolen rakenteet kullekin tasolle:
// PAdES-perusallekirjoituskenttä (ETSI EN 319 142-1)
Pdf.CurrentPage.AddPAdESSignatureField(
'ApprovalSig', Rect(50, 100, 350, 150), 'B-B',
'Contract approval', 'Boston, MA', 'legal@example.com');
// Asiakirjan aikaleima: suurempi varaus TSA-tunnisteelle ja -ketjulle
Pdf.CurrentPage.AddDocumentTimestampSignature('ArchiveTS', 16384);
Aikaleiman 16384 tavun varaus on tarkoituksellinen. Aikaleimaviranomainen palauttaa tunnisteen, joka vetää oman varmenneketjunsa perässään, joten se tarvitsee säännöllisesti enemmän tilaa kuin 8 kt, joihin tavallinen allekirjoitus on tyytyväinen. Nämä asiakirjan aikaleimat ovat myös koneisto B-LTA:n takana: arkistoidun allekirjoituksen säännöllinen uudelleenaikaleimaus muutaman vuoden välein edelleen ajankohtaisilla algoritmeilla on se, mikä pitää vuonna 2026 allekirjoittamasi asiakirjan todennettavana vuonna 2040
Sanat syy-, sijainti- ja yhteysmerkkijonoista, jotka molemmat kenttäpuhelut hyväksyvät: ne ovat mukavuusmetatietoja ja ei muuta. HotPDF tallentaa ne tavallisina sanakirjamerkintöinä ja maalaa ne näkyvään allekirjoituksen ulkonäköön, mutta mikään vahvistaja ei tarkista niitä mitään vastaan. Täytä ne johdonmukaisesti työnkulun tiedoista, koska tilintarkastajat lukevat niitä, ja sitten et koskaan pidä niitä todisteena. Varsinainen salausväite elää täysin CMS:ssä ja sen varmenneketjussa, ja varmentaja jättää näkyvän tekstin kokonaan huomiotta
Allekirjoittamisen jälkeen tiedosto saa vain kasvaa
Hetken, kun allekirjoitus on olemassa, sen alueiden sisällä olevat tavut jäädytetään. Ainoa oikeutettu tapa muuttaa tiedostoa jälkikäteen on standardin ISO 32000-1 §7.5.6 mukainen lisäpäivitys, joka liittää uudet ja muutetut objektit alkuperäisten tavujen perään ja ketjuttaa niihin uuden ristiviittausosan. Tällä tavalla allekirjoitus pysyy voimassa sen tarkistuksen osalta, ja katsoja ilmoittaa rehellisen tilan: allekirjoitettu revisio on ehjä, asiakirjaa jatkettiin jälkikäteen. Sarjoita koko tiedosto sen sijaan uudelleen ja kirjoitat allekirjoitetut kärkivälit uudelleen, mikä tuhoaa allekirjoituksen silloinkin, kun mitään näkyvää ei ole muuttunut. Saman versiointimekanismin kautta yhdessä asiakirjassa on myös useita allekirjoituksia: jokainen uusi allekirjoitus laskeutuu omaan inkrementaaliseen päivitykseensä, ja sen alueet kattavat kaiken sitä ennen olevan, myös aiemmat allekirjoitukset. Pelkän lisäyksen mekaniikka, ja kun ne on turvallista tiivistää, käsitellään artikkelissa objektivirroista ja inkrementaalisista päivityksistä
Kaksi rajaa on syytä pitää mielessäsi suunnittelun aikana. HotPDF:n PDF/A-tulostustila hylkää allekirjoituskentät kokonaan, joten arkiston yhteensopivuus ja upotettu allekirjoitus on toimitettava erillisinä tiedostoina. Ja allekirjoittaminen ei sano mitään salaisuudesta: se todistaa, kuka on tuottanut asiakirjan ja että se ei ole muuttunut sen jälkeen, mutta kuka tahansa voi silti lukea sen. Sisällön salaaminen on erillinen tehtävä, jota hoitaa AES-256-salaus- ja oikeuskäytäntö
Mitä tahansa rakennatkin, testaa se jollakin muulla kuin koodilla, joka kirjoitti tiedoston. Avaa tuloste Acrobatin allekirjoituspaneelissa ja vahvista kolme asiaa: allekirjoitus on voimassa, identiteetti ketjutetaan odottamaasi juureen, ja paneeli ilmoittaa, ettei allekirjoittamisen jälkeen ole tapahtunut muutoksia. Käännä sitten yksittäinen tavu poiskopioitavan kopion allekirjoitetulla alueella ja varmista, että paneeli kutsuu asiakirjaa muutetuksi. Allekirjoitusputki, jota et ole koskaan nähnyt hylkäävän peukaloitua tiedostoa, on sellainen, jonka vahvistusta ei ole varsinaisesti testattu
Kaikki kolme allekirjoitustasoa toimitetaan Delphin ja C++Builderin HotPDF Component -komponentin mukana; tuotesivulla on linkki täydelliseen allekirjoituksen API-viitteeseen