Tekninen artikkeli

PDFium VCL: etäallekirjoitus PAdES HSM- ja pilviavaimin

PDFiumPas jakaa PAdES-allekirjoituksen kahteen kutsuun, jotta yksityisen avaimen ei koskaan tarvitse olla prosessissasi. PreparePadesRemoteSignature kirjoittaa inkrementaalisen päivityksen, jossa on tyhjä kiinteäleveyksinen /Contents-paikkamerkki, ja palauttaa pyyntötietueen, joka kantaa SHA-256-asiakirjatiivisteen, täsmällisen ByteRange-arvon ja valmistellun tiedoston sormenjäljen. CompletePadesRemoteSignature ottaa allekirjoituspalvelusi palauttaman irrotetun CMS:n ja pudottaa sen tuohon varattuun paikkaan

Näiden kahden kutsun välillä voi kulua minuutteja tai tunteja, prosessi voi käynnistyä uudelleen, ja työ voi siirtyä toiselle koneelle. Tuo aukko on koko syy siihen, miksi API on muotoiltu tällä tavalla

Miksi etäavain ei voi käyttää tavallista allekirjoituskutsua?

Koska SignPadesBytes olettaa, että allekirjoitusoperaatio tapahtuu kutsun sisällä. Se rakentaa inkrementaalisen päivityksen, laskee tiivisteen ByteRange-alueen yli, allekirjoittaa sen ja kirjoittaa tuloksen, kaiken tämän ennen paluuta. Tämä on täsmälleen oikein, kun avain asuu Windowsin varmennevarastossa tai lataamassasi PKCS#12-tiedostossa

Se on mahdotonta, kun avain asuu verkko-HSM:ssä, luottamuspalvelun tarjoajan käyttämässä pätevässä allekirjoituksenluontilaitteessa tai pilviallekirjoitus-API:ssa, joka vaatii käyttäjää vahvistamaan puhelimella. Näissä tapauksissa sekvenssi ei ole funktiokutsu, se on keskustelu: lähetät tiivisteen, jokin muu todentaa ihmisen, ja CMS palaa myöhemmin. Synkroninen API ei voi ilmaista "myöhemmin" ilman, että se lukitsee säikeen operaatioon, joka saattaa tarvita toisen tekijän

Kaksivaiheinen protokolla

Vaihe yksi valmistelee asiakirjan. PDFiumPas liittää allekirjoituskentän ja arvosanakirjan, varaa ContentsSize tavua heksakoodattua tilaa kentässä /Contents, laskee ByteRange-arvon tuon varauksen ympärille ja tuottaa TPadesRemoteSigningRequest-tietueen, joka sisältää FormatVersion-, PreparedFingerprint-, DocumentDigest-arvot, nelosaisen ByteRange-arvon sekä ContentsHexOffset- ja ContentsSize-arvot

Ainoa arvo, jonka allekirjoituspalvelusi tarvitsee, on DocumentDigest: SHA-256, jonka palautetun CAdES SignedData -rakenteen täytyy kantaa viestitiivisteenään. Kaikki muu tietueessa on olemassa, jotta vaihe kaksi voi todistaa, että tiedosto, jota se viimeistelee, on se tiedosto, josta tuo tiiviste laskettiin

uses
  FPdfPades;

var
  Options: TPadesRemoteSignOptions;
  Request: TPadesRemoteSigningRequest;
  Source, Prepared, Session: TFileStream;
begin
  Options := TPadesRemoteSignOptions.Default;
  Options.Reason := 'Approved by finance';
  Options.Location := 'Lisbon';
  Options.Name := 'A. Moreira';
  Options.SigningTimeUtc := NowUtc;
  Options.ContentsSize := 16384;   // CMS:lle varatut heksatavut

  Source := TFileStream.Create('contract.pdf', fmOpenRead or fmShareDenyWrite);
  Prepared := TFileStream.Create('contract.prepared.pdf', fmCreate);
  try
    PreparePadesRemoteSignature(Source, Prepared, Options, Request);
  finally
    Prepared.Free;
    Source.Free;
  end;

  // Tallenna istunto, jotta myöhempi ajo - tai toinen kone - voi viimeistellä sen
  Session := TFileStream.Create('contract.signreq', fmCreate);
  try
    SavePadesRemoteSigningRequest(Session, Request);
  finally
    Session.Free;
  end;

  SendDigestToSigningService(Request.DocumentDigest);
end;

Minkä Complete hylkää, ja miksi jokainen tarkistus on olemassa?

Viimeistely on kohta, jossa etäallekirjoitussuunnittelu yleensä menee pieleen, joten validointi on tarkoituksella anteeksiantamaton. CompletePadesRemoteSignature hylkää valmistellun PDF:n, jonka sormenjälki ei enää täsmää pyynnön kanssa, ByteRange-arvon, joka ei täsmää tallennettujen paikkamerkin koordinaattien kanssa, muokatut /Contents-rajaimet, paikkamerkin, joka ei ole enää tyhjä, varausta suuremman CMS:n, CMS:n, joka ei ole täsmälleen yksi DER-arvo, tuetun ulkopuolisen SignedData-muodon, puuttuvan signing-certificate-v2-attribuutin, sekä CMS:n, jonka viestitiiviste ei vastaa valmistellun asiakirjan tiivistettä

Jokainen niistä vastaa todellista virhetilaa. Sormenjälki- ja ByteRange-tarkistukset havaitsevat tapauksen, jossa joku on generoinut valmistellun tiedoston uudelleen vaiheiden välissä, mikä tuottaisi allekirjoituksen, joka varmentuu tavuja vasten, joita kellään ei ole. Tyhjän paikkamerkin tarkistus havaitsee kaksinkertaisen viimeistelyn, jossa toinen CMS kirjoitetaan jo olemassa olevan allekirjoituksen päälle. Viestitiivisteen tarkistus havaitsee kaikkein vaarallisimman tapauksen: oikein muodostetun CMS:n, joka on allekirjoitettu eri asiakirjan yli, mikä on se, mitä saat, kun jono sekoittaa kaksi samanaikaista allekirjoitusistuntoa. Ilman sitä tuottaisit tiedoston, joka näyttää allekirjoitetulta ja epäonnistuu validoinnissa kaikkialla, tai pahempaa, joka kantaa jonkun muun hyväksyntää

Signing-certificate-v2-vaatimus on PAdES-yhteensopivuusasia eikä eheysasia. ETSI EN 319 142 vaatii, että allekirjoitusvarmenne sidotaan allekirjoitettuihin attribuutteihin, ja CMS, josta tuo attribuutti puuttuu, ei ole PAdES-allekirjoitus, vaikka se varmentuisikin kryptografisesti. Sen hylkääminen viimeistelyssä tarkoittaa, että saat tietää tämän täällä, et validaattoriraportista asiakkaalta, aihetta käsitellään laajemmin artikkelissa miksi validaattorit hylkäävät PAdES-allekirjoituksia

var
  Request: TPadesRemoteSigningRequest;
  Session, Prepared, Dest: TFileStream;
  CmsDer: TBytes;
begin
  Session := TFileStream.Create('contract.signreq', fmOpenRead);
  try
    Request := LoadPadesRemoteSigningRequest(Session);
  finally
    Session.Free;
  end;

  CmsDer := FetchDetachedCmsFromService;   // HSM:n tai TSP:n palauttama

  Prepared := TFileStream.Create('contract.prepared.pdf', fmOpenRead);
  Dest := TFileStream.Create('contract.signed.pdf', fmCreate);
  try
    try
      CompletePadesRemoteSignature(Prepared, Dest, Request, CmsDer);
    except
      on E: EPadesCrypto do
        // Jokainen hylkäys kantaa täsmällisen syyn; kirjaa se sellaisenaan
        FailSession(E.Message);
    end;
  finally
    Dest.Free;
    Prepared.Free;
  end;
end;

Prosessi- ja konerajojen ylitys

SavePadesRemoteSigningRequest ja LoadPadesRemoteSigningRequest serialisoivat istunnon vakaan versioidun binäärimuodon kautta, mikä on se, mikä tekee suunnittelusta käytännöllisen eikä vain oikean. Web-sovellus voi valmistella asiakirjan yhdessä pyynnössä, tallentaa valmistellun PDF:n ja istuntoblobin, palauttaa tiivisteen selaimelle älykorttiallekirjoitusta varten ja viimeistellä tiedoston täysin eri pyyntökäsittelijässä

FormatVersion-kenttä on se, mikä pitää tämän turvallisena päivitysten yli. Vanhemman buildin kirjoittama ja uudemman lataama istunto tunnistetaan tai hylätään eksplisiittisesti sen sijaan, että se luettaisiin väärin toisenmuotoisena tietueena. Jos jonosi voi pitää istuntoja päiviä, käsittele muotoversiota operatiivisena faktana, joka kannattaa kirjata lokiin, ei toteutusyksityiskohtana

Paikkamerkin mitoitus

ContentsSize on se yksi parametri, jota täytyy miettiä, koska se on kiinnitetty ennen kuin CMS on olemassa. Se laskee heksakoodatun varauksen, joten 6 kt:n DER-CMS tarvitsee vähintään 12 kt tilaa, ja toteutus rajaa varauksen 64 MiB:iin

Varaa liian vähän, ja viimeistely epäonnistuu liian-suuri-CMS-virheeseen sen jälkeen, kun allekirjoituspalvelusi on jo tehnyt työnsä, mikä mitatulla pätevän allekirjoituksen palvelulla tarkoittaa hukattua operaatiota. Varaa liikaa, ja jokainen allekirjoitettu asiakirja kantaa täytettä ikuisesti. Järkevä lähestymistapa on mitata: allekirjoita yksi asiakirja todellisella varmenneketjullasi, katso DER-pituus, kaksinkertaista se heksaa varten, ja lisää sitten runsaasti tilaa aikaleimatunnukselle, jos aiot päivittää T-tason allekirjoitukseen. Ketjut, joissa on useita välivarmenteita ja pitkä OCSP-vastaus, kasvavat nopeammin kuin odotetaan

Mitä allekirjoituksen jälkeen tapahtuu

Valmis etäallekirjoitus on PAdES B-B. Pitkäaikainen validointi tarvitsee aikaleiman ja validointimateriaalin, mikä on erillinen inkrementaalinen päivitys, joka lisää DSS:n ja sen allekirjoituskohtaiset VRI-sanakirjat, kuvattu artikkelissa pitkäaikaiset allekirjoitukset RFC 3161 -aikaleimoilla ja DSS:llä. Tuo vaihe on paikallinen: se lisää varmenteita, OCSP-vastauksia ja CRL-listoja, joista mikään ei tarvitse yksityistä avainta

Ennen toimitusta varmenna se, mitä tuotit, samalla koodipolulla, jota luottava osapuoli käyttäisi, käsitelty artikkelissa digitaalisten allekirjoitusten ja PAdES-tasojen tarkastelu. Allekirjoitus ja varmennus ovat eri koodia, ja etäallekirjoitusputki on juuri se paikka, jossa nämä kaksi voivat ajautua erilleen kenenkään huomaamatta, ennen kuin ulkoinen validaattori kertoo siitä

PDFiumPas on Delphi- ja Lazarus-komponentti PDFium-moottorin ympärillä natiivilla Pascal-PAdES-pinolla, joten allekirjoitus, aikaleimaus ja validointi toimivat ilman ulkoisia komentorivityökaluja. Täydellinen API-dokumentaatio ja kokeiluversio löytyvät sivulta PDFium Delphi component page