Teknisk artikel

Langsigtede PDF-signaturer i Delphi: PAdES B-LT og B-LTA

For at få en signeret PDF til at kunne verificeres mange år senere, efter at signeringscertifikatet er udløbet og dets CA er skiftet ud, kan PDFium Component generere langtids-PAdES-signaturer på Windows: et RFC 3161-tidsstempel for troværdig tid, et Document Security Store, der indlejrer valideringsmaterialet, og et arkiv-DocTimeStamp, der dækker hele filen. Disse tre tilføjelser er PAdES-baseline-niveauerne B-T, B-LT og B-LTA, og PDFium Component når dem alle fra ét enkelt signeringskald. Dette er en Windows-only funktion, styret af PadesCryptoAvailable

Denne artikel er den tredje i en serie. Den første, signering af PDF'er med PAdES B-B ved hjælp af PDFium VCL, dækker den grundlæggende signatur; den anden, inspektion af PDF-digitale signaturer og PAdES-niveauer, dækker indlæsning af en signatur igen og bestemmelse af hvilket niveau den nåede. Her handler det om arkivet: signaturer der skal overleve certifikatudløb og forblive verificerbare i en opbevaringsperiode målt i år, ikke uger

Hvorfor holder en gyldig PDF-signatur op med at kunne verificeres over tid?

En grundlæggende signatur besvarer kun ét spørgsmål: er disse bytes blevet ændret efter signeringen. Den beviser ikke i sig selv, hvornår signeringen fandt sted, og det er netop det hul, der ødelægger den senere. CMS-strukturen indeholder et signeringstidsattribut, men underskriveren skriver den værdi ud fra sit eget ur, så en validator har ingen grund til at stole på den. Når signeringscertifikatet senere udløber, eller CA'en tilbagekalder det, kan en streng validator ikke længere skelne en signatur, der blev lavet mens certifikatet var gyldigt, fra en der er forfalsket efter udløbet. Signaturen var i orden den dag, den blev lavet, og bliver uverificerbar uden nogen fejl fra dens egen side

Langtidsarkivering løser dette med to greb. For det første fastslår en betroet tredjepart, en Time Stamping Authority, tiden kryptografisk, så den ikke længere afhænger af underskriverens ærlighed. For det andet indlejres hvert certifikat, hver tilbagekaldelsesrespons og hver CRL, der er nødvendig for at opbygge og kontrollere tillidskæden, i selve PDF'en, så validering ikke afhænger af servere, der måske er væk, når nogen kigger efter. PAdES, standardiseret som ETSI EN 319 142-1, lagdeler disse som baseline-niveauer, og PDFium Component skriver de strukturer, hvert niveau kræver

Hvad er en LTV- eller arkiv-tidsstemplet PDF-signatur?

PAdES definerer fire baseline-niveauer, der bygger oven på hinanden, og PDFium Component eksponerer dem via enum'en TPadesLevel (plB_B, plB_T, plB_LT, plB_LTA). B-B er den rene signatur. B-T tilføjer et troværdigt tidsstempel over signaturværdien. B-LT (long-term) tilføjer det indlejrede valideringsmateriale, den egenskab de fleste mener med »LTV«. B-LTA (long-term med arkiv-tidsstempel) pakker det hele ind i endnu et tidsstempel, der dækker hele filen, så det indlejrede materiale selv kan bevises intakt fra arkiveringstidspunktet

Den praktiske læsning er en trappe. B-T beviser hvornår. B-LT beviser hvormed signaturen stadig kan kontrolleres. B-LTA beviser, at hele pakken ikke er blevet manipuleret siden den blev forseglet, og det er det niveau, man fornyer, ved at tilføje et nyt DocTimeStamp, før det forrige udløber, for at forlænge arkivet på ubestemt tid. For en kontrakt eller et compliance-dokument med en lang opbevaringspligt er B-LTA målet; B-LT er det pragmatiske gulv

Tilføjelse af troværdig tid: PAdES B-T med RFC 3161

PDFium Component omdanner en B-B-signatur til B-T ved at sætte to felter på TPadesSignOptions: hæv Level til plB_T, og angiv en TsaUrl. Når begge er til stede, hasher signeringspipelinen de rå signaturoktetter med SHA-256, pakker det digest som RFC 3161-messageImprint'et, POST'er anmodningen til TSA'en via WinHttp og indlejrer det returnerede token som det usignerede signature-time-stamp-attribut (OID 1.2.840.113549.1.9.16.2.14) i SignerInfo's unsignedAttrs [1], jf. EN 319 142-1 §6.3. Tidsstemplet dækker specifikt signaturværdien, hvilket er det, der binder den troværdige tid til netop den signatur

uses
  PDFium, FPdfPades;

procedure SignWithTimestamp;
var
  Pdf: TPdf;
  Options: TPadesSignOptions;
begin
  Pdf := TPdf.Create(nil);
  try
    if not Pdf.PadesCryptoAvailable then
      raise Exception.Create('PAdES signing backend is Windows-only');
    Pdf.FileName := 'contract.pdf';
    Pdf.Active := True;

    Options := TPadesSignOptions.Default;
    Options.CertificateThumbprint := 'A1B2C3D4E5F6071829304152637485960A1B2C3D';
    Options.Level  := plB_T;
    Options.TsaUrl := 'http://timestamp.example-tsa.com/tsr';
    Options.Reason := 'Contract execution';
    // Nonce efterlades på 0: komponenten udleder en unik anti-replay-værdi.

    Pdf.SignPades('contract-bt.pdf', Options);
  finally
    Pdf.Free;
  end;
end;

CertificateThumbprint er SHA-1-hashen, i hex, af et certifikat i Current User-butikken »MY«, hvis private nøgle du kan bruge. Feltet Nonce er RFC 3161-anti-replay-værdien; lad den stå på nul, så udleder komponenten en unik værdi pr. anmodning ud fra en SHA-256-foldning af en tæller, uret, tick count'en og proces-id'et, så to anmodninger udstedt inden for samme millisekund ikke kolliderer. Angiv kun din egen værdi, hvis du har en kryptografisk RNG, du hellere vil stole på. Én ærlig hage: B-T kræver en tilgængelig TSA, så signering foretager nu et netværkskald og arver TSA'ens tilgængelighed og latenstid

Indlejring af valideringsmateriale: B-LT og B-LTA i ét kald

Trinnet op til langtidsvalidering er én enkelt ændring: sæt Level til plB_LTA, og behold TsaUrl. PDFium Component gennemløber derefter hele trappen inden i ét enkelt SignPades-kald. Den signerer B-B, tidsstempler for B-T, injicerer Document Security Store til B-LT og tilføjer arkiv-tidsstemplet til B-LTA, hver som sin egen inkrementelle opdatering, så de tidligere bytes forbliver byte-for-byte intakte

procedure SignForArchive;
var
  Pdf: TPdf;
  Options: TPadesSignOptions;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'contract.pdf';
    Pdf.Active := True;

    Options := TPadesSignOptions.Default;
    Options.CertificateThumbprint := 'A1B2C3D4E5F6071829304152637485960A1B2C3D';
    Options.Level    := plB_LTA;   // driver B-B -> B-T -> B-LT -> B-LTA
    Options.TsaUrl   := 'http://timestamp.example-tsa.com/tsr';
    Options.Location := 'Head Office';

    // Ét kald skriver tidsstemplet, DSS'en og arkiv-DocTimeStamp'et.
    Pdf.SignPades('contract-lta.pdf', Options);
  finally
    Pdf.Free;
  end;
end;

Til B-LT-trinnet opbygger PDFium Component certifikatkæden med CertGetCertificateChain og skriver en /DSS-dictionary, der bærer underskriverens certifikat plus hver mellemliggende CA i et /Certs-array (med parallelle /OCSPs- og /CRLs-arrays), sammen med en /Extensions /ESIC-markør på niveau 1, præcis som EN 319 142-1 §5.4.2 foreskriver. Den selvsignerede rod udelades bevidst, da RFC 5652 §10.2.3 tillader det, og roden allerede er et tillidsanker, validatoren har. Resultatet er en signatur, en læser kan verificere med intet andet end filen i hånden

Til B-LTA tilføjer komponenten et Document Time-stamp: en signature dictionary med /SubFilter /ETSI.RFC3161, hvis /ByteRange spænder over hele filen, hvis /Contents indeholder et RFC 3161-token over det interval, og som forfremmer ESIC-markøren til niveau 2. Dette er arkivtrinnet fra §5.4.3 og TS 119 142-3. Fordi DocTimeStamp'et dækker både DSS'en og signaturen, beviser det, at det indlejrede valideringsmateriale var til stede og uændret på arkiveringstidspunktet, hvilket er præcis det, en lang opbevaringspolitik skal kunne påvise

Udvidelse af en signatur, der allerede findes

Nogle gange er signaturen allerede på dokumentet, og du skal kun tilføje eller opdatere valideringsstoren, for eksempel for at bringe en tredjeparts B-B-signatur op til B-LT til dit arkiv. PDFium Component eksponerer DSS-injektoren direkte via SaveAsPadesDss, som tilføjer strukturerne /DSS og /Extensions som en inkrementel opdatering uden at røre de eksisterende signaturbytes

procedure AddValidationStore;
var
  Pdf: TPdf;
  DssOpts: TPadesDssOptions;
  Cert: TPadesDssMaterial;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'already-signed.pdf';
    Pdf.Active := True;

    DssOpts := TPadesDssOptions.Default;
    DssOpts.EsicExtensionLevel := 1;         // 1 til B-LT, 2 til B-LTA
    Cert.DerBytes := LoadSignerCertDer;       // dit DER-kodede certifikat
    SetLength(DssOpts.Certs, 1);
    DssOpts.Certs[0] := Cert;

    Pdf.SaveAsPadesDss('already-signed-lt.pdf', DssOpts);
  finally
    Pdf.Free;
  end;
end;

Du angiver de DER-kodede certifikater og, hvis relevant, OCSP-svar og CRL'er, som en validator får brug for. Sæt EsicExtensionLevel til 1 for et B-LT-store eller 2, når et DocTimeStamp følger efter. Lad IncludeVri stå på sin standard False: EN 319 142-1 §5.4.2.3 behandler den per-signatur-/VRI-dictionary som valgfri og fraråder den, medmindre en specifik læser, du målretter mod, kræver det

Omkostninger og grænser, det er værd at planlægge for

Tre ærlige begrænsninger former, hvor disse niveauer passer ind. For det første er den kryptografiske backend Windows-only: signering og tidsstempling går gennem platformens CNG- og WinHttp-butikker, så PadesCryptoAvailable returnerer False andre steder, og SignPades rejser en exception. Læsesiden med inspektion, som dækkes i følgeartiklen, forbliver platformuafhængig; kun skrivningen af signaturer er bundet til Windows. For det andet afhænger B-T og B-LTA af en Time Stamping Authority, hvilket betyder en netværks-roundtrip på signeringstidspunktet og en ekstern tjeneste, hvis oppetid og rate limits bliver en del af din signeringssti

For det tredje koster hvert niveau lagerplads. Hvert trin tilføjer en inkrementel opdatering i stedet for at omskrive filen, DSS'en indlejrer en fuld certifikatkæde, og arkiv-DocTimeStamp'et reserverer plads til sit token, så en B-LTA-fil er mærkbart større end B-B-originalen. Det er den bevidste handel: du bruger bytes nu for at købe verificerbarhed senere. For de fleste kontrakt- og compliance-arkiver er det den rigtige handel, men det er værd at måle på dine egne dokumenter i stedet for at antage det. Når du skal bekræfte, at en færdig fil rent faktisk nåede det niveau, du tilsigtede, så verificér den med teknikkerne i inspektion af PDF-digitale signaturer og PAdES-niveauer, og hvis du hærder en dokumentpipeline mere bredt, dækker noterne om audit af PDF-sikkerhedsrisici de omkringliggende kontroller

PAdES-signering, DSS og arkiv-tidsstempel-funktionerne, der vises her, er en del af PDFium Component til Delphi og C++Builder, hvis produktside indeholder den fulde signeringsreference og platformkravene