Artigo Técnico

Assinaturas PDF de Longo Prazo no Delphi: PAdES B-LT e B-LTA

Para garantir que um PDF assinado continue a ser verificado anos a partir de agora, após o certificado de assinatura expirar e a sua CA (Autoridade de Certificação) ser renovada, o PDFium Component pode produzir assinaturas PAdES de longo prazo em Windows: um carimbo de data/hora (timestamp) RFC 3161 para hora fidedigna (trusted time), um Document Security Store (DSS) que incorpora os dados de validação, e um DocTimeStamp de arquivo sobre todo o ficheiro. Essas três adições correspondem aos níveis de referência PAdES B-T, B-LT e B-LTA, e o PDFium Component alcança todos a partir de uma chamada única de assinatura. Esta é uma capacidade exclusiva do Windows, condicionada pela PadesCryptoAvailable

Este artigo é o terceiro de uma série. O primeiro, assinar PDFs com PAdES B-B utilizando o PDFium VCL, aborda a assinatura básica; o segundo, inspecionar assinaturas digitais PDF e níveis PAdES, cobre a leitura de uma assinatura e a identificação do nível que atingiu. Aqui a preocupação é o arquivo: assinaturas que devem sobreviver à expiração do certificado e permanecer verificáveis durante um período de retenção medido em anos e não em semanas

Por que uma assinatura PDF válida deixa de ser verificado ao longo do tempo?

Uma assinatura básica responde apenas a uma pergunta: se estes bytes foram alterados após a assinatura. Ela não prova, por si só, quando a assinatura ocorreu, e essa lacuna é o que causa problemas mais tarde. A estrutura CMS contém um atributo de hora de assinatura, mas o signatário escreve esse valor a partir do seu próprio relógio, pelo que um validador não tem motivos para confiar nele. Quando o certificado de assinatura expira posteriormente ou a CA o revoga, um validador rigoroso deixa de conseguir distinguir uma assinatura feita enquanto o certificado era válido de uma forjada após o certificado ter caducado. A assinatura estava correta no dia em que foi efetuada e torna-se não verificável sem qualquer culpa própria

O arquivamento a longo prazo corrige isto em duas etapas. Primeiro, uma entidade terceira fidedigna, uma Autoridade de Carimbo de Data/Hora (TSA), atesta o momento de forma criptográfica, para que este deixe de depender da honestidade do signatário. Segundo, todos os certificados, respostas de revogação e CRL necessários para construir e verificar a cadeia de confiança são incorporados dentro do próprio PDF, pelo que a validação não depende de servidores que podem já não existir quando alguém os for consultar. O PAdES, normalizado como ETSI EN 319 142-1, organiza estas etapas como níveis de referência, e o PDFium Component escreve as estruturas exigidas por cada nível

O que é uma assinatura PDF LTV ou com carimbo de data/hora de arquivo?

O PAdES define quatro níveis de referência que se complementam, e o PDFium Component expõe-nos através da enumeração TPadesLevel (plB_B, plB_T, plB_LT, plB_LTA). O B-B é a assinatura simples. O B-T adiciona um carimbo de data/hora (timestamp) fidedigno sobre o valor da assinatura. O B-LT (long-term) adiciona os dados de validação incorporados, a propriedade a que a maioria das pessoas se refere como "LTV". O B-LTA (long-term com carimbo de data/hora de arquivo) envolve tudo com mais um carimbo que cobre todo o ficheiro, de modo que os próprios dados incorporados permanecem comprovadamente intactos a partir do momento de arquivamento

A perspetiva prática assemelha-se a uma escada. O B-T prova quando. O B-LT prova com o quê a assinatura pode continuar a ser verificada. O B-LTA prova que todo o pacote não foi adulterado desde que foi selado, e é o nível que deve renovar, adicionando um novo DocTimeStamp antes que o anterior expire, de forma a prolongar o arquivo indefinidamente. Para um contrato ou um registo de conformidade com uma obrigação de retenção longa, o B-LTA é o objetivo; o B-LT é o patamar pragmático

Adicionar hora fidedigna: PAdES B-T com RFC 3161

O PDFium Component transforma uma assinatura B-B em B-T definindo dois campos em TPadesSignOptions: alterando o Level para plB_T e fornecendo um TsaUrl. Quando ambos estão presentes, o pipeline de assinatura processa os octetos da assinatura em bruto com a hash SHA-256, empacota esse resumo como o messageImprint do RFC 3161, envia o pedido (POST) para a TSA através de WinHttp e incorpora o token devolvido como o atributo não assinado signature-time-stamp (OID 1.2.840.113549.1.9.16.2.14) no SignerInfo unsignedAttrs [1], em conformidade com a norma EN 319 142-1 §6.3. O carimbo de data/hora cobre especificamente o valor da assinatura, o que vincula o momento fidedigno a essa assinatura exata

uses
  PDFium, FPdfPades;

procedure SignWithTimestamp;
var
  Pdf: TPdf;
  Options: TPadesSignOptions;
begin
  Pdf := TPdf.Create(nil);
  try
    if not Pdf.PadesCryptoAvailable then
      raise Exception.Create('O motor de assinatura PAdES é exclusivo para Windows');
    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 mantido em 0: o componente deriva um valor único contra repetição (anti-replay).

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

O CertificateThumbprint é o hash SHA-1, em hexadecimal, de um certificado no repositório "MY" do Utilizador Atual cuja chave privada pode utilizar. O campo Nonce é o valor anti-repetição do RFC 3161; se o mantiver como zero, o componente deriva um valor único por pedido a partir de uma combinação SHA-256 de um contador, o relógio, a contagem de ciclos (tick count) e o ID do processo, de modo a evitar colisões entre dois pedidos emitidos no mesmo milissegundo. Forneça o seu próprio valor apenas quando dispõe de um gerador de números aleatórios (RNG) criptográfico de confiança. Uma advertência sincera: o nível B-T necessita de uma TSA acessível, pelo que a assinatura passa agora a efetuar uma chamada de rede, herdando a disponibilidade e a latência da TSA

Incorporar dados de validação: B-LT e B-LTA numa única chamada

A passagem para a validação a longo prazo constitui uma alteração única: defina o Level como plB_LTA e mantenha a TsaUrl. O PDFium Component executa então a sequência completa dentro de uma única chamada SignPades. Assina o nível B-B, aplica o carimbo para B-T, injeta o Document Security Store para B-LT e anexa o carimbo de data/hora de arquivo para B-LTA, cada um como a sua própria atualização incremental para que os bytes anteriores permaneçam intactos byte a 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;   // orienta o fluxo B-B -> B-T -> B-LT -> B-LTA
    Options.TsaUrl   := 'http://timestamp.example-tsa.com/tsr';
    Options.Location := 'Head Office';

    // Uma chamada grava o carimbo de data/hora, a DSS e o DocTimeStamp de arquivo.
    Pdf.SignPades('contract-lta.pdf', Options);
  finally
    Pdf.Free;
  end;
end;

Para a fase B-LT, o PDFium Component constrói a cadeia de certificados com CertGetCertificateChain e escreve um dicionário /DSS que transporta o certificado do signatário e todas as AC intermédias numa matriz /Certs (com matrizes paralelas /OCSPs e /CRLs), juntamente com um marcador /Extensions /ESIC no Nível 1, exatamente como prescreve a norma EN 319 142-1 §5.4.2. A raiz autoassinada é deliberadamente omitida, visto que o RFC 5652 §10.2.3 o permite e a raiz já representa uma âncora de confiança detida pelo validador. O resultado é uma assinatura que um leitor pode verificar dispondo apenas do ficheiro

Para o B-LTA, o componente anexa um Document Time-stamp: um dicionário de assinaturas com /SubFilter /ETSI.RFC3161 cuja /ByteRange abrange todo o ficheiro, contendo a sua /Contents um token RFC 3161 sobre esse intervalo, e promove o marcador ESIC ao Nível 2. Este é o passo de arquivo descrito no §5.4.3 e na norma TS 119 142-3. Como o DocTimeStamp cobre tanto a DSS como a assinatura, prova que os dados de validação incorporados estavam presentes e inalterados no momento do arquivamento, que é exatamente o que uma política de retenção longa deve conseguir comprovar

Melhorar uma assinatura que já existe

Por vezes, a assinatura já se encontra no documento e apenas necessita de adicionar ou atualizar o repositório de validação, por exemplo, para elevar uma assinatura B-B de terceiros para o nível B-LT para o seu arquivo. O PDFium Component expõe o injetor DSS diretamente através de SaveAsPadesDss, que anexa as estruturas /DSS e /Extensions como uma atualização incremental, sem alterar os bytes da assinatura existente

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 para B-LT, 2 para B-LTA
    Cert.DerBytes := LoadSignerCertDer;       // o seu certificado codificado em DER
    SetLength(DssOpts.Certs, 1);
    DssOpts.Certs[0] := Cert;

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

Fornece os certificados codificados em DER e, opcionalmente, respostas OCSP e CRLs de que um validador necessitará. Defina a EsicExtensionLevel como 1 para um repositório B-LT ou como 2 quando se segue um DocTimeStamp. Mantenha a IncludeVri no seu padrão False: a norma EN 319 142-1 §5.4.2.3 trata o dicionário /VRI por assinatura como opcional e desaconselha-o, exceto se exigido por um leitor específico para o qual desenvolva

Custos e limites que vale a pena planear

Três limites honestos moldam a aplicação destes níveis. Primeiro, o backend criptográfico é exclusivo para Windows: a assinatura e a aplicação do carimbo de data/hora correm através do CNG da plataforma e das bibliotecas WinHttp, pelo que a PadesCryptoAvailable devolve False noutros sistemas e a SignPades gera falhas. A inspeção do lado da leitura abordada no artigo complementar mantém-se multiplataforma; apenas a gravação de assinaturas está associada ao Windows. Segundo, os níveis B-T e B-LTA dependem de uma Autoridade de Carimbo de Data/Hora (TSA), o que implica uma chamada de rede no momento da assinatura e um serviço externo cuja disponibilidade e limites de taxa passam a integrar o seu processo de assinatura

Terceiro, cada nível consome armazenamento. Cada etapa anexa uma atualização incremental em vez de reescrever o ficheiro, a DSS incorpora uma cadeia completa de certificados e o DocTimeStamp de arquivo reserva espaço para o seu token, pelo que um ficheiro B-LTA é significativamente maior do que o original B-B. Trata-se de uma compensação deliberada: consome bytes agora para garantir a verificabilidade futura. Para a maioria dos arquivos de conformidade e contratos, é a escolha correta, mas vale a pena efetuar medições nos seus próprios documentos em vez de assumir conclusões. Quando necessitar de confirmar se um ficheiro concluído atingiu efetivamente o nível pretendido, verifique-o com as técnicas descritas em inspecionar assinaturas digitais PDF e níveis PAdES e, se estiver a reforçar a segurança de um pipeline de documentos de forma mais abrangente, as notas sobre auditar riscos de segurança PDF cobrem as restantes verificações

As funcionalidades de assinatura PAdES, DSS e carimbo de data/hora de arquivo apresentadas aqui fazem parte do PDFium Component para Delphi e C++Builder, cuja página de produto contém a referência completa de assinatura e os requisitos da plataforma