Un PDF appena firmato è una firma B-B e nient'altro. Prova chi ha firmato e che i byte non si sono mossi, ma non porta alcuna prova che il certificato del firmatario fosse valido al momento della firma, così un validatore tra anni dovrà andare a cercare dati di revoca che potrebbero non esistere più. Colmare quel divario significa scrivere risposte OCSP e CRL nel Document Security Store a livello di documento, e in HotPDF è una chiamata: PopulatePAdESLTVEvidence percorre ogni firma caricata, deriva le richieste di revoca dall'insieme di certificati, le esegue attraverso un trasporto che Lei fornisce, e scrive il materiale recuperato più la catena CMS nel DSS. Restituisce il numero di firme di cui la prova è atterrata, o meno uno quando il documento non ha alcun campo firma
La decisione di progetto che vale la pena comprendere prima di usarla è che la libreria non apre mai un socket. Ogni byte che arriva dalla rete arriva attraverso una callback scritta da Lei. Non è prudenza per se stessa; è l'unico modo in cui questa funzione può lavorare dentro gli ambienti che davvero esigono la validazione a lungo termine
Perché la libreria rifiuta di fare HTTP da sola?
Perché i posti che esigono firme B-LT sono i posti in cui a una libreria non si può affidare la rete. I servizi di firma girano dietro proxy con autenticazione e radici aziendali. I livelli di firma air-gapped non hanno alcuna strada verso un responder e devono essere nutriti con prove in cache. I regimi di audit richiedono che ogni richiesta in uscita sia registrata dall'applicazione, non sepolta in una dipendenza. E le suite di test hanno bisogno di risposte deterministiche, il che è impossibile se la libreria chiama fuori per conto suo
Il trasporto è un semplice riferimento a funzione con una forma fissa, così la politica resta di Lei. HotPDF consegna un record di richiesta che descrive esattamente cosa recuperare, incluso il content type e un tetto alla dimensione della risposta, e Lei restituisce i byte più uno stato
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 dice se si tratta di un POST OCSP o di un GET CRL;
// Request.ContentType e Request.Body sono già preparati,
// e Request.MaxResponseBytes è il tetto che Lei deve rispettare
Response := HttpExchange(Request.URI, Request.ContentType,
Request.Body, Request.MaxResponseBytes);
Result := setsSucceeded;
except
on E: Exception do
begin
ErrorMessage := E.Message;
// setsRetry lascia indietreggiare la politica di retry; usare
// setsPermanentFailure per un 404 o un URL sbagliato
Result := setsRetry;
end;
end;
end;
// Upgrade B-B a B-LT in una chiamata per ogni firma nel file caricato
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
// Salvataggio solo-append: i byte che le firme esistenti coprono
// sono preservati verbatim
Pdf.SaveIncrementalUpdate('signed-lt.pdf');
finally
Pdf.Free;
end;
end;
I guasti sono per firma, non per documento. Un responder che va in timeout per un firmatario salta il materiale di quel firmatario e lascia intatto il resto della passata, che è il comportamento voluto in un batch: una prova parziale batte un'esecuzione abortita, e il valore di ritorno dice quante firme siano effettivamente migliorate
La catena che il CMS ha dimenticato di includere
La verifica delle revoche richiede il certificato dell'issuer, e un numero sorprendente di stack di firma omette gli intermedi dal contenitore CMS. La via di recupero è l'estensione Authority Information Access, metodo di accesso 1.3.6.1.5.5.7.48.2, che pubblicizza un URL da cui scaricare il certificato dell'issuer. HPDFFetchAIAIntermediates percorre quegli URL attraverso lo stesso trasporto, estrae il DER da ogni risposta e restituisce solo i certificati che il CMS non portava già, chiave per hash DER così duplicati e cicli non possono girare
Due dettagli decidono se questo funziona contro vere certificate authority. Il primo è la codifica: gli endpoint delle CA servono il certificato come DER nudo più o meno spesso quanto lo servono con armatura PEM, e non c'è un content type affidabile per distinguerli. La sonda robusta è testuale, poi strutturale. Cercare il marcatore -----BEGIN CERTIFICATE-----, togliere l'armatura e decodificare il base64 se presente, e in entrambe le vie confermare che il primo byte del risultato sia $30, il tag DER di una SEQUENCE. Il secondo è la profondità: un intermedio recuperato può a sua volta pubblicizzare un URL AIA per il proprio issuer, quindi il percorso aggiunge nuovi candidati alla coda e completa catene corte di due o tre salti. Va posto un tetto, che è ciò per cui esiste il parametro MaxFetch
Che cos'è un seed value di firma e perché fallisce in silenzio?
Un seed value è un vincolo che l'autore del documento aggancia a un campo firma per dire al firmatario che tipo di firma è accettabile: quale SubFilter, quale algoritmo di digest, quali motivazioni, quale versione minima del PDF, se l'informazione di revoca debba essere incorporata. Vive in un dizionario /SV sul campo ed è definito in ISO 32000-1 §12.7.5.5. HotPDF lo scrive con AttachPAdESSeedValue e lo verifica con CheckLoadedSignatureSeedValue, che restituisce True quando il campo è privo di vincoli o ogni vincolo presente passa, e su False nomina il primo vincolo fallito attraverso un parametro di output da mettere direttamente in un messaggio di errore
Il meccanismo che rende i seed value facili da sbagliare è la voce di flag /Ff descritta in §12.7.5.5.3. Un bit a 1 marca il proprio vincolo come richiesto: una mancata corrispondenza è un errore e il firmatario deve rifiutare. Un bit a 0 marca lo stesso vincolo come preferenza: il valore filtra ciò che la UI dovrebbe offrire e nient'altro. Da ciò seguono due trappole. Primo, /Ff vive dentro il dizionario /SV, non sull'annotazione widget, quindi codice che legge il /Ff a livello di campo ottiene per sempre una risposta vuota e conclude che nulla è obbligatorio. Secondo, le assegnazioni dei bit non sono una semplice sequenza di uno, due, quattro, otto; in HotPDF il writer emette 2 per SubFilter, 4 per MinVersion, 32 per AddRevInfo e 64 per DigestMethod. Un lettore che assume bit sequenziali decodifica ogni vincolo come opzionale e passa ogni test tranne quello che conta
var
Violation: AnsiString;
begin
// Chiedere al campo se il profilo con cui stiamo per firmare è consentito
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));
// Vincolo soddisfatto: procedere con la passata di firma
end;
Il test che ha esposto il bug di decodifica originale non era un test positivo. Era l'asserzione che una mancata corrispondenza obbligatoria debba essere rifiutata, ed è l'unico tipo di test in grado di cogliere questa classe di difetti: un decoder che legge il dizionario sbagliato o le posizioni di bit sbagliate produce "nessun vincolo violato" per ogni input, il che sembra esattamente comportamento corretto finché non se ne viola uno deliberatamente
Dove si colloca sulla scala LTV
Quattro gradini, e ognuno richiede quello sotto di sé. B-B è la firma nuda. B-T aggiunge un timestamp fidato, che fissa l'ora di firma così un validatore sa rispetto a quale momento valutare la revoca. B-LT aggiunge le prove di revoca al DSS, che è ciò che PopulatePAdESLTVEvidence automatizza. B-LTA aggiunge timestamp di documento rinnovati prima che il precedente indebolisca, estendendo la validità indefinitamente; HotPDF lo espone come RenewPAdESLTATimestamp, che aggiunge un nuovo timestamp come revisione incrementale e preserva intatte ogni firma, timestamp e voce DSS precedenti
Un modello ad aggiornamento incrementale è l'unico modo corretto di aggiungere prove a un documento firmato, perché riscrivere il file romperebbe i byte range che le firme esistenti coprono. Se occorre ragionare su cosa sia cambiato tra revisioni, e se quelle modifiche siano del tipo che una firma consente, quell'analisi è trattata separatamente nell'analisi delle revisioni DocMDP e FieldMDP. La pipeline di firma in sé, incluse sorgenti di certificati e insidie di ordine dei byte, è nel percorso di firma PAdES, e il lato validazione è in verifica delle firme sui documenti caricati
Un avvertimento pratico sull'ordinamento. Raccogliere le prove appena possibile dopo la firma, idealmente nello stesso lavoro. I responder che possono rispondere per un certificato sono online finché il certificato è corrente e spariti anni dopo, così un documento che esce dalla pipeline di Lei come B-B potrebbe non essere mai più aggiornabile. HotPDF gira come componente VCL nativo per Delphi e C++Builder, e l'intera passata di prove è in-process a parte il trasporto di Lei; i profili supportati sono elencati nella pagina di prodotto di HotPDF Delphi PDF component