Teknisk artikel

PAdES LTV-beviser og seed values i HotPDF

En PDF, du lige har signeret, er en B-B-signatur og ikke mere. Den beviser, hvem der signede, og at bytene ikke er flyttet, men den bærer intet bevis på, at underskrivercertifikatet var gyldigt på signeringstidspunktet, så en validator mange år senere må lede efter tilbagekaldelsesdata, der muligvis ikke længere findes. At lukke det hul betyder at skrive OCSP-svar og CRL'er ind i det dokumentniveau Document Security Store, og i HotPDF er det ét kald: PopulatePAdESLTVEvidence gennemløber hver indlæst signatur, afleder tilbagekaldelsesanmodningerne ud fra certifikatsættet, udfører dem gennem en transport, du leverer, og skriver det hentede materiale plus CMS-kæden ind i DSS. Den returnerer antallet af signaturer, hvis beviser landede, eller minus én, når dokumentet slet ikke har et signaturfelt

Designbeslutningen, der er værd at forstå, før du bruger den, er, at biblioteket aldrig åbner en socket. Hver byte, der ankommer fra netværket, ankommer gennem et callback, du skrev. Det er ikke forsigtighed for sin egen skyld; det er den eneste måde, denne funktion kan virke inde i de miljøer, der reelt kræver langtidsvalidering

Hvorfor nægter biblioteket at lave sin egen HTTP?

Fordi de steder, der kræver B-LT-signaturer, er de steder, hvor et bibliotek ikke kan betroes netværket. Signeringstjenester kører bag autentificerende proxies med virksomhedens root-certifikater. Air-gapped signeringstier har ingen rute til en responder og skal fodres med cachede beviser. Auditregimer kræver, at hver udgående anmodning logges af applikationen, ikke begraves i en afhængighed. Og testsuiter behøver deterministiske svar, hvilket er umuligt, hvis biblioteket selv ringer ud

Transporten er en almindelig funktionsreference med fast form, så politikken forbliver din. HotPDF giver dig en request-record, der beskriver præcis, hvad der skal hentes, inklusive content-typen og et svarstørrelsestag, og du returnerer bytes plus en status

HotPDF PopulatePAdESLTVEvidence-flow: den kalder-leverede FetchEvidence-transport, request-record-felter og statusudfald pr. signatur
Hver netværksbyte passerer gennem dit FetchEvidence-callback, og hver signatur får sin egen status, så ét timeout aldrig afbryder gennemløbet
function FetchEvidence(const Request: THPDFSignatureEvidenceRequest;
  Attempt: Integer; CancellationToken: THPDFCancellationToken;
  out Response: TBytes; out RetryAfterMS: Cardinal;
  out ErrorMessage: UnicodeString): THPDFSignatureEvidenceTransportStatus;
begin
  RetryAfterMS := 0;
  try
    // Request.Kind siger, om dette er en OCSP POST eller en CRL GET;
    // Request.ContentType og Request.Body er allerede forberedt,
    // og Request.MaxResponseBytes er det loft, du må overholde
    Response := HttpExchange(Request.URI, Request.ContentType,
      Request.Body, Request.MaxResponseBytes);
    Result := setsSucceeded;
  except
    on E: Exception do
    begin
      ErrorMessage := E.Message;
      // setsRetry lader retry-politikken trække sig tilbage; brug
      // setsPermanentFailure til en 404 eller en dårlig URL
      Result := setsRetry;
    end;
  end;
end;

// Ét-kalds B-B til B-LT-opgradering for hver signatur i den indlæste fil
var
  Pdf: THotPDF;
  Upgraded: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.BeginIncrementalUpdate('signed.pdf');
    Upgraded := Pdf.PopulatePAdESLTVEvidence(FetchEvidence,
      THPDFSignatureEvidenceRetryPolicy.Default);
    if Upgraded > 0 then
      // Append-only-gemning: byterne, som de eksisterende signaturer
      // dækker, bevares ordret
      Pdf.SaveIncrementalUpdate('signed-lt.pdf');
  finally
    Pdf.Free;
  end;
end;

Fejl er pr. signatur, ikke pr. dokument. En responder, der timeout for én underskriver, springer den underskrivers materiale over og efterlader resten af gennemløbet intakt, hvilket er den adfærd, du vil have i en batch: delvise beviser slår en afbrudt kørsel, og returværdien fortæller dig, hvor mange signaturer, der reelt forbedredes

Kæden, som CMS glemte at inkludere

Tilbagekaldelsestjek behøver issuer-certifikatet, og et overraskende antal signeringsstakke udelader intermediater fra CMS-containeren. Genfindingsvejen er Authority Information Access-udvidelsen, access method 1.3.6.1.5.5.7.48.2, som annoncerer en URL, hvor issuer-certifikatet kan downloades. HPDFFetchAIAIntermediates gennemløber de URL'er gennem samme transport, parser DER ud af hvert svar og returnerer kun de certifikater, som CMS ikke allerede bar, nøglet efter DER-hash, så duplikater og løkker ikke kan dreje rundt

To detaljer afgør, om dette virker mod rigtige certifikatautoriteter. Den første er kodning: CA-endpoints serverer certifikatet som bar DER omtrent lige så ofte, som de serverer det PEM-rustet, og der er ingen pålidelig content-type til at skelne dem. Den robuste sonde er tekstlig, derefter strukturel. Kig efter -----BEGIN CERTIFICATE------markøren, strip rustningen og dekod base64, hvis den er til stede, og i begge veje bekræft, at resultatets første byte er $30, DER-tag'et for en SEQUENCE. Den anden er dybde: et hentet intermedier kan selv annoncere en AIA-URL for sin egen issuer, så gennemløbet tilføjer nye kandidater til køen og fuldfører kæder, der mangler to eller tre hop. Det skal tages på, og det er det, parameteren MaxFetch er til

AIA-kædefuldførelsesdiagram for HotPDF: caIssuers URL-hentning, PEM versus DER-sonde, DER-hash-deduplikering og MaxFetch-dybdetaget
HPDFFetchAIAIntermediates gennemløber caIssuers-URL'er gennem samme transport, sonder PEM-rustning og tager køen på med MaxFetch

Hvad er en signatur-seed value, og hvorfor fejler den i stilhed?

En seed value er en begrænsning, som dokumentforfatteren knytter til et signaturfelt for at fortælle underskriveren, hvilken slags signatur der er acceptabel: hvilken SubFilter, hvilken digest-algoritme, hvilke grunde, hvilken minimums-PDF-version, om tilbagekaldelsesinformation skal indlejres. Den bor i en /SV-ordbog på feltet og er defineret i ISO 32000-1 §12.7.5.5. HotPDF skriver den med AttachPAdESSeedValue og tjekker den med CheckLoadedSignatureSeedValue, som returnerer True, når feltet er ubegrænset, eller alle tilstedeværende begrænsninger består, og ved False navngiver den første fejlende begrænsning gennem en outputparameter, du kan lægge direkte ind i en fejlmeddelelse

Mekanismen, der gør seed values lette at tage fejl af, er /Ff-flags-posten beskrevet i §12.7.5.5.3. En sat bit markerer sin begrænsning som påkrævet: en uoverensstemmelse er en fejl, og underskriveren må nægte. En nulstillet bit markerer samme begrænsning som en præference: værdien filtrerer, hvad UI'en bør tilbyde, og ikke mere. To fælder følger af det. For det første bor /Ff inde i /SV-ordbogen, ikke på widget-annotationen, så kode, der læser feltniveauets /Ff, får et tomt svar for altid og konkluderer, at intet er tvunget. For det andet er bittildelingerne ikke en simpel række af én, to, fire, otte; i HotPDF udsender writeren 2 for SubFilter, 4 for MinVersion, 32 for AddRevInfo og 64 for DigestMethod. En læser, der antager sekventielle bits, dekoder hver begrænsning som valgfri og består hver test undtagen den, der tæller

Seed value-flagbittabel til HotPDF PAdES-signering, der viser Ff-bits 2, 4, 32 og 64 samt påkrævet versus foretrukken begrænsningshåndtering
/Ff-posten bor inde i /SV, og hver bitposition afgør, om en uoverensstemmelse er en hård afvisning eller en UI-præference
var
  Violation: AnsiString;
begin
  // Spørg feltet, om profilen, vi er ved at signere med, er tilladt
  if not Pdf.CheckLoadedSignatureSeedValue(0, 'ETSI.CAdES.detached',
       'SHA256', 'Approved for payment', 1, Violation) then
    raise Exception.Create('Signature field rejects this profile: ' +
      String(Violation));
  // Begrænsning opfyldt: fortsæt signeringsgennemløbet
end;

Testen, der afslørede den oprindelige dekodefejl, var ikke en positiv test. Det var assertionen om, at en tvunget uoverensstemmelse må afvises, og det er den eneste slags test, der kan fange denne klasse af defekter: en dekoder, der læser den forkerte ordbog eller de forkerte bitpositioner, producerer "ingen begrænsninger brudt" for hvert input, hvilket ser præcis ud som korrekt adfærd, indtil du med vilje bryder én

Hvor dette ligger på LTV-stigen

Fire trin, og hvert behøver det under sig. B-B er den bare signatur. B-T tilføjer et betroet tidsstempel, som fastlægger signeringstidspunktet, så en validator ved, hvilket øjeblik tilbagekaldelse skal evalueres mod. B-LT tilføjer tilbagekaldelsesbeviserne til DSS, hvilket er det, PopulatePAdESLTVEvidence automatiserer. B-LTA tilføjer dokumenttidsstempler, der fornyes, før det foregående svækkes, og udvider gyldigheden på ubestemt tid; HotPDF eksponerer det som RenewPAdESLTATimestamp, som tilføjer et nyt tidsstempel som en inkrementel revision og bevarer hver tidligere signatur, tidsstempel og DSS-post urørt

En inkrementel opdateringsmodel er den eneste korrekte måde at tilføje beviser til et signeret dokument, for at omskrive filen ville bryde de byte-intervaller, de eksisterende signaturer dækker. Hvis du har brug for at ræsonnere om, hvad der ændredes mellem revisioner, og om de ændringer er af den slags, en signatur tillader, er den analyse dækket separat i DocMDP- og FieldMDP-revisionsanalysen. Signeringspipelinen selv, inklusive certifikatkilder og byte-ordre-fælder, er i PAdES-signeringsgennemgangen, og valideringssiden er i verificering af signaturer på indlæste dokumenter

Én praktisk advarsel om rækkefølge. Indsaml beviser så hurtigt som muligt efter signering, ideelt i samme job. Responderne, der kan svare for et certifikat, er online, mens certifikatet er aktuelt, og væk år senere, så et dokument, der forlader din pipeline som B-B, kan muligvis aldrig opgraderes igen. HotPDF kører som en native VCL-komponent til Delphi og C++Builder, og hele bevisgennemløbet er in-process bortset fra din egen transport; de understøttede profiler er opført på produktsiden HotPDF Delphi PDF component