En PDF du just signerat är en B-B-signatur och ingenting mer. Den bevisar vem som signerade och att bytena inte har flyttat sig, men den bär inget bevis för att undertecknarcertifikatet var giltigt vid signeringstiden, så en validator flera år senare måste leta efter spärrdata som kanske inte längre finns. Att stänga det gapet betyder att skriva OCSP-svar och CRL:er till dokumentnivåns Document Security Store, och i HotPDF är det ett anrop: PopulatePAdESLTVEvidence går igenom varje inläst signatur, härleder spärrbegäran från certifikatmängden, utför dem genom en transport du tillhandahåller och skriver det hämtade materialet plus CMS-kedjan till DSS. Den returnerar antalet signaturer vars bevis landade, eller minus ett när dokumentet inte har något signaturfält alls
Designbeslutet värt att förstå innan du använder den är att biblioteket aldrig öppnar en socket. Varje byte som anländer från nätverket anländer genom ett callback du skrev. Det är inte försiktighet för dess egen skull; det är enda sättet denna funktion kan fungera inuti de miljöer som faktiskt kräver långtidsvalidering
Varför vägrar biblioteket köra sin egen HTTP?
För att platserna som kräver B-LT-signaturer är platserna där ett bibliotek inte kan ges förtroende för nätverket. Signeringstjänster körs bakom autentiserande proxyservrar med företagets egna rotcertifikat. Luftgapande signeringstjänstnivåer har ingen väg till en responder och måste matas med cachade bevis. Revisionsregimer kräver att varje utgående begäran loggas av applikationen, inte begravas i ett beroende. Och testsviter behöver deterministiska svar, vilket är omöjligt om biblioteket ringer ut på egen hand
Transporten är en ren funktionsreferens med fast form, så policyn förblir din. HotPDF räcker dig en begäranpost som beskriver exakt vad som ska hämtas, inklusive innehållstypen och ett tak för svarets storlek, och du returnerar bytena plus en status
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 säger huruvida detta är en OCSP POST eller en CRL GET;
// Request.ContentType och Request.Body är redan förberedda,
// och Request.MaxResponseBytes är taket du måste hedra
Response := HttpExchange(Request.URI, Request.ContentType,
Request.Body, Request.MaxResponseBytes);
Result := setsSucceeded;
except
on E: Exception do
begin
ErrorMessage := E.Message;
// setsRetry låter återförsökspolicyn backa; använd
// setsPermanentFailure för en 404 eller en dålig URL
Result := setsRetry;
end;
end;
end;
// Ett-anrops B-B till B-LT-uppgradering för varje signatur i den inlästa filen
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-sparande: bytena de befintliga signaturerna täcker
// bevaras ordagrant
Pdf.SaveIncrementalUpdate('signed-lt.pdf');
finally
Pdf.Free;
end;
end;
Misslyckanden är per signatur, inte per dokument. En responder som gör timeout för en undertecknare hoppar över den undertecknarens material och lämnar resten av passet intakt, vilket är beteendet du vill ha i en batch: partiella bevis slår en avbruten körning, och returvärdet talar om hur många signaturer som faktiskt förbättrades
Kedjan som CMS glömde att inkludera
Spärrkontroll behöver utfärdarcertifikatet, och förvånansvärt många signeringsstackar utelämnar mellan-certifikat från CMS-containern. Återhämtningsvägen är utökningen Authority Information Access, åtkomstmetod 1.3.6.1.5.5.7.48.2, som utlyser en URL där utfärdarcertifikatet kan laddas ner. HPDFFetchAIAIntermediates går igenom de URL:erna via samma transport, parsar DER ur varje svar och returnerar bara de certifikat CMS inte redan bar, nycklade efter DER-hash så att dubbletter och loopar inte kan snurra
Två detaljer avgör om detta fungerar mot riktiga certifikatutfärdare. Den första är kodning: CA-slutpunkter serverar certifikatet som ren DER ungefär lika ofta som de serverar det PEM-bepansrat, och det finns ingen tillförlitlig innehållstyp att skilja dem åt. Den robusta sonden är textuell, sedan strukturell. Leta efter markören -----BEGIN CERTIFICATE-----, avlägsna bepansringen och avkoda base64 om den finns, och i båda vägarna bekräfta att första byten av resultatet är $30, DER-taggen för en SEQUENCE. Den andra är djup: ett hämtat mellan-certifikat kan själv utlysa en AIA-URL för sin egen utfärdare, så vandringen lägger till nya kandidater till kön och kompletterar kedjor som är två eller tre hopp korta. Det måste takas, vilket är vad parametern MaxFetch är till för
Vad är en signatur-seed value, och varför misslyckas den tyst?
En seed value är ett villkor dokumentförfattaren fäster vid ett signaturfält för att tala om för undertecknaren vilken typ av signatur som är acceptabel: vilken SubFilter, vilken sammandragsalgoritm, vilka skäl, vilken minsta PDF-version, huruvida spärrinformation måste bäddas in. Den bor i en /SV-ordlista på fältet och är definierad i ISO 32000-1 §12.7.5.5. HotPDF skriver den med AttachPAdESSeedValue och kontrollerar den med CheckLoadedSignatureSeedValue, som returnerar True när fältet är obegränsat eller varenda befintligt villkor passeras, och vid False namnger det första fallerande villkoret genom en utdataparameter du kan lägga rakt in i ett felmeddelande
Mekanismen som gör seed values lätta att få fel är /Ff-flaggposten som beskrivs i §12.7.5.5.3. En sättad bit märker sitt villkor som obligatoriskt: en avvikelse är ett fel och undertecknaren måste vägra. En nollställd bit märker samma villkor som en preferens: värdet filtrerar vad användargränssnittet ska erbjuda och inget mer. Två fällor följer av det. För det första bor /Ff inuti /SV-ordlistan, inte på widget-annotationen, så kod som läser fältnivåns /Ff får ett tomt svar för alltid och drar slutsatsen att inget är tvingande. För det andra är bitfördelningen inte en enkel följd av ett, två, fyra, åtta; i HotPDF avger skrivaren 2 för SubFilter, 4 för MinVersion, 32 för AddRevInfo och 64 för DigestMethod. En läsare som antar sekventiella bitar avkodar vartdera villkoret som valfritt och passerar varje test utom det som betyder något
var
Violation: AnsiString;
begin
// Fråga fältet huruvida profilen vi är på väg att signera med är tillåten
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));
// Villkoret uppfyllt: fortsätt med signeringspasset
end;
Testet som avslöjade den ursprungliga avkodningsbuggen var inte ett positivt test. Det var assertionen att ett tvingat avvikande värde måste avvisas, och det är den enda sorts test som kan fånga denna klass av defekt: en avkodare som läser fel ordlista eller fel bitpositioner ger inga villkor brutna för varje indata, vilket ser exakt ut som korrekt beteende tills du med flit bryter mot ett
Var detta ligger på LTV-stegen
Fyra steg, och vart och ett behöver det under sig. B-B är den nakna signaturen. B-T lägger till en betrodd tidsstämpel, som fixerar signeringstiden så att en validator vet vilket ögonblick spärrar ska utvärderas mot. B-LT lägger till spärrbevisen till DSS, vilket är vad PopulatePAdESLTVEvidence automatiserar. B-LTA lägger till dokumenttidstämplar som förnyas innan den föregående försvagas, vilket utökar giltigheten på obestämd tid; HotPDF exponerar det som RenewPAdESLTATimestamp, som bifogar en ny tidsstämpel som en inkrementell revision och bevarar varje tidigare signatur, tidsstämpel och DSS-post orörd
En inkrementell uppdateringsmodell är det enda korrekta sättet att lägga bevis till ett signerat dokument, eftersom att skriva om filen skulle bryta de byteintervall de befintliga signaturerna täcker. Om du behöver resonera om vad som ändrats mellan revisioner, och huruvida de ändringarna är den sort en signatur tillåter, tas den analysen upp separat i DocMDP- och FieldMDP-revisionsanalysen. Signeringspipelinen själv, inklusive certifikatkällor och byteordningsfällor, finns i genomgången av PAdES-signering, och valideringssidan finns i verifiering av signaturer på inlästa dokument
En praktisk varning om ordning. Samla bevis så snart som möjligt efter signeringen, helst i samma jobb. Responderarna som kan svara för ett certifikat är online medan certifikatet är aktuellt och borta år senare, så ett dokument som lämnar din pipeline som B-B kanske aldrig kan uppgraderas igen. HotPDF körs som en nativ VCL-komponent för Delphi och C++Builder, och hela bevispasset är inuti processen utom din egen transport; de stödda profilerna listas på produktsidan för HotPDF Delphi PDF component