Teknisk artikel

Långsiktiga PDF-signaturer i Delphi: PAdES B-LT och B-LTA

För att göra en signerad PDF verifierbar år från nu, efter att signeringscertifikatet har löpt ut och dess utfärdare (CA) har roterat, kan PDFium Component producera långsiktiga PAdES-signaturer på Windows: en RFC 3161-tidsstämpel för betrodd tid, ett Document Security Store (DSS) som bäddar in valideringsmaterialet, och en arkiv-DocTimeStamp över hela filen. Dessa tre tillägg är basnivåerna PAdES B-T, B-LT och B-LTA, och PDFium Component når dem alla från ett enda signeringsanrop. Detta är en funktion som endast är tillgänglig på Windows, begränsad av PadesCryptoAvailable

Den här artikeln är den tredje i en serie. Den första, signera PDF-filer med PAdES B-B med PDFium VCL, täcker den grundläggande signaturen; den andra, inspektera digitala PDF-signaturer och PAdES-nivåer, beskriver hur man läser tillbaka en signatur och ser vilken nivå den nådde. Här ligger fokus på arkivet: signaturer som måste överleva att certifikat löper ut och förbli verifierbara under en lagringsperiod som mäts i år, inte veckor

Varför slutar en giltig PDF-signatur att verifieras över tid?

En grundläggande signatur svarar bara på en fråga: ändrades dessa byte efter signeringen. Den bevisar inte i sig själv när signeringen skedde, och det glappet är vad som bryter den senare. CMS-strukturen bär på ett signerings-tidsattribut, men undertecknaren skriver det värdet från sin egen klocka, så en validerare har ingen anledning att lita på det. När signeringscertifikatet senare löper ut eller CA återkallar det kan en strikt validerare inte längre skilja en signatur som gjorts medan certifikatet var giltigt från en som förfalskats efter att det upphört. Signaturen var bra den dag den gjordes och blir overifierbar utan att det är dess eget fel

Långsiktig arkivering åtgärdar detta i två steg. Först intygar en betrodd tredje part, en tidsstämplingsmyndighet (TSA), tiden kryptografiskt så att den inte längre beror på undertecknarens ärlighet. För det andra bäddas varje certifikat, återkallelsesvar och CRL som behövs för att bygga och kontrollera förtroendekedjan in i själva PDF-filen, så att valideringen inte beror på servrar som kan vara borta när någon tittar. PAdES, standardiserat som ETSI EN 319 142-1, delar in dessa som basnivåer, och PDFium Component skriver de strukturer som varje nivå kräver

Vad är en LTV- eller arkivtidsstämplad PDF-signatur?

PAdES definierar fyra basnivåer som bygen på varandra, och PDFium Component exponerar dem via TPadesLevel-uppräkningen (plB_B, plB_T, plB_LT, plB_LTA). B-B är den vanliga signaturen. B-T lägger till en betrodd tidsstämpel över signaturvärdet. B-LT (long-term) lägger till det inbäddade valideringsmaterialet, den egenskap som de flesta menar med "LTV". B-LTA (long-term med arkivtidsstämpel) omsluter allt i ytterligare en tidsstämpel som täcker hela filen, så att det inbäddade materialet i sig bevisligen är intakt från stunden för arkiveringen

Den praktiska tolkningen är en trappa. B-T bevisar när. B-LT bevisar med vad signaturen fortfarande kan kontrolleras. B-LTA bevisar att hela paketet inte har manipulerats sedan det förseglades, och det är den nivå du förnyar genom att lägga till en ny DocTimeStamp innan den föregående löper ut, för att förlänga arkivet på obestämd tid. För ett kontrakt eller ett efterlevnadsregister med en lång lagringsskyldighet är B-LTA målet; B-LT är det pragmatiska golvet

Lägga till betrodd tid: PAdES B-T med RFC 3161

PDFium Component förvandlar en B-B-signatur till B-T genom att ställa in två fält i TPadesSignOptions: höj Level till plB_T och ange en TsaUrl. När båda finns på plats hashar signeringspipelinen de råa signatur-oktetterna med SHA-256, paketerar det sammandraget som meddelandeavtryck (messageImprint) enligt RFC 3161, skickar begäran (POST) till TSA över WinHttp och bäddar in den returnerade tokenen som det osignerade attributet signature-time-stamp (OID 1.2.840.113549.1.9.16.2.14) i SignerInfos unsignedAttrs [1], i enlighet med EN 319 142-1 §6.3. Tidsstämpeln täcker specifikt signaturvärdet, vilket är det som binder den betrodda tiden till just den signaturen

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 left at 0: the component derives a unique anti-replay value.

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

CertificateThumbprint är SHA-1-hashen, i hexadecimal form, av ett certifikat i den aktuella användarens "MY"-lager vars privata nyckel du kan använda. Fältet Nonce är anti-återspelningsvärdet (anti-replay) enligt RFC 3161; lämna det som noll så härleder komponenten ett unikt värde per begäran från en SHA-256-vikning av en räknare, klockan, tick-antalet och process-id, så att två förfrågningar som utfärdas under samma millisekund inte kolliderar. Ange ett eget värde endast när du har en kryptografisk slumptalsgenerator (RNG) som du hellre litar på. Ett ärligt förbehåll: B-T behöver en nåbar TSA, så signering innebär nu ett nätverksanrop och ärver TSA:s tillgänglighet och fördröjning

Bädda in valideringsmaterial: B-LT och B-LTA i ett anrop

Steget upp till långsiktig validering är en enda ändring: ställ in Level till plB_LTA och behåll TsaUrl. PDFium Component kör då hela trappan inuti ett enda SignPades-anrop. Den signerar B-B, tidsstämplar för B-T, injicerar Document Security Store för B-LT och lägger till arkivtidsstämpeln för B-LTA, var och en som sin egen inkrementella uppdatering så att de tidigare byten förblir intakta byte för byte

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;   // drives B-B -> B-T -> B-LT -> B-LTA
    Options.TsaUrl   := 'http://timestamp.example-tsa.com/tsr';
    Options.Location := 'Head Office';

    // One call writes the timestamp, the DSS and the archive DocTimeStamp.
    Pdf.SignPades('contract-lta.pdf', Options);
  finally
    Pdf.Free;
  end;
end;

För B-LT-steget bygger PDFium Component certifikatkedjan med CertGetCertificateChain och skriver en /DSS-ordbok som bär undertecknarens certifikat plus varje mellanliggande CA i en /Certs-matris (med parallella /OCSPs och /CRLs-matriser), tillsammans med en /Extensions /ESIC-markör på nivå 1, exakt som EN 319 142-1 §5.4.2 föreskriver. Den självsignerade roten utelämnas medvetet, eftersom RFC 5652 §10.2.3 tillåter det och roten redan är ett förtroendeankare som valideraren har. Resultatet är en signatur som en läsare kan verifiera enbart med filen i hand

För B-LTA lägger komponenten till en dokumenttidsstämpel (Document Time-stamp): en signaturordbok med /SubFilter /ETSI.RFC3161 vars /ByteRange spänner över hela filen, dess /Contents som håller en RFC 3161-token över det intervallet, och den främjar ESIC-markören till nivå 2. Detta är arkiveringssteget i §5.4.3 och TS 119 142-3. Eftersom DocTimeStamp täcker DSS såväl som signaturen bevisar den att det inbäddade valideringsmaterialet fanns där och var oförändrat vid tidpunkten för arkiveringen, vilket är precis vad en långsiktig lagringspolicy måste kunna visa

Komplettera en signatur som redan finns

Ibland finns signaturen redan på dokumentet och du behöver bara lägga till eller uppdatera valideringslagret, till exempel för att höja en tredjeparts B-B-signatur till B-LT för ditt arkiv. PDFium Component exponerar DSS-injiceraren direkt via SaveAsPadesDss, som lägger till strukturerna /DSS och /Extensions som en inkrementell uppdatering utan att röra de befintliga signaturbyten

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 for B-LT, 2 for B-LTA
    Cert.DerBytes := LoadSignerCertDer;       // your DER-encoded certificate
    SetLength(DssOpts.Certs, 1);
    DssOpts.Certs[0] := Cert;

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

Du tillhandahåller de DER-kodade certifikaten, och valfritt OCSP-svar och CRL:er, som en validerare behöver. Ställ in EsicExtensionLevel till 1 för ett B-LT-lager eller 2 när en DocTimeStamp kommer att följa. Lämna IncludeVri på sitt standardvärde False: EN 319 142-1 §5.4.2.3 behandlar den per-signatur-ordboken /VRI som valfri och avråder från den om inte en specifik läsare du riktar in dig på kräver det

Kostnader och gränser värda att planera för

Tre ärliga gränser formar var dessa nivåer passar. För det första är den kryptografiska motorn endast för Windows: signering och tidsstämpling går via plattformens CNG- och WinHttp-lager, så PadesCryptoAvailable returnerar False på andra ställen och SignPades utlöser fel. Den inspektion på lässidan som beskrevs i den tillhörande artikeln förblir plattformsoberoende; endast skrivningen av signaturer är bunden till Windows. För det andra beror B-T och B-LTA på en tidsstämplingsmyndighet (TSA), vilket innebär en nätverksrunda vid signeringstillfället och en extern tjänst vars upptid och anropsgränser blir en del av din signeringsväg

För det tredje kostar varje nivå lagringsutrymme. Varje steg lägger till en inkrementell uppdatering snarare än att skriva om filen, DSS bäddar in en hel certifikatkedja och arkiv-DocTimeStamp reserverar utrymme för sin token, så en B-LTA-fil är betydligt större än B-B-originalet. Det är den medvetna avvägningen: du spenderar byte nu för att köpa verifierbarhet senare. För de flesta kontrakts- och efterlevnadsarkiv är det rätt avvägning, men det är värt att mäta på dina egna dokument istället för att anta saker. När du behöver bekräfta att en färdig fil faktiskt nådde den nivå du avsåg verifierar du den med teknikerna i inspektera digitala PDF-signaturer och PAdES-nivåer, och om du härdar en dokumentpipeline mer allmänt täcker anteckningarna om granskning av PDF-säkerhetsrisker de omgivande kontrollerna

Funktionerna för PAdES-signering, DSS och arkivtidsstämpel som visas här är en del av PDFium Component för Delphi och C++Builder, vars produktsida innehåller hela signeringsreferensen och plattformskraven