Como as assinaturas se encaixam em um PDF
ISO 32000-2 §12.8 Spec: ETSI EN 319 142-1 ETSI EN 319 142-1 Spec: RFC 5652 RFC 5652 Evidence: Standard-backed
Em resumo
Seção intitulada “Em resumo”Uma assinatura de PDF não fica em volta do arquivo. Ela é embutida dentro dele: um dicionário que identifica a assinatura e um digest calculado sobre um intervalo declarado de bytes, ignorando deliberadamente o próprio valor da assinatura. Esta página explica esse mecanismo e, com a mesma importância, o que ele não promete.
Por que isso importa
Seção intitulada “Por que isso importa”“O documento está assinado” é uma frase que as pessoas usam para tomar decisões. Elas a associam a um pagamento, uma aprovação ou uma obrigação legal. Se você não sabe exatamente quais bytes uma assinatura cobre, não consegue dizer o que um resultado válido realmente prova. Um PDF pode conter uma assinatura perfeitamente válida e, ainda assim, exibir ao leitor conteúdo que o signatário nunca viu, porque esse conteúdo foi adicionado depois da assinatura, em uma região que ela nunca reivindicou. Saber onde a autoridade da assinatura começa e termina é a diferença entre uma decisão defensável e uma decisão baseada em suposição.
A versão curta
Seção intitulada “A versão curta”- Uma assinatura de PDF reside em um dicionário de assinatura e em um campo de assinatura dentro do documento, não como um envelope externo.
- Os bytes assinados são declarados por um array
ByteRange: dois segmentos(offset, length)que, juntos, cobrem o arquivo inteiro exceto o valor hexadecimal da assinatura contido na entradaContents. - O digest desses dois segmentos concatenados é o que a assinatura criptográfica realmente protege.
- Tudo o que for anexado posteriormente em uma nova revisão fica fora do byte range original. A assinatura original permanece válida; ela nunca fez nenhuma afirmação sobre os novos bytes.
- Uma assinatura de aprovação e uma assinatura de certificação diferem em escopo: a certificação (DocMDP) restringe quais alterações posteriores são permitidas; a aprovação não.
Como o NextPDF aborda isso
Seção intitulada “Como o NextPDF aborda isso”O NextPDF constrói a assinatura de acordo com o modelo do formato, em uma ordem fixa, para que o byte range seja exato, não aproximado.
Quando o engine grava uma assinatura, primeiro reserva um espaço de tamanho fixo para o valor
Contents e grava um placeholder ByteRange de largura fixa. Ele
aguarda até que o documento completo seja gravado, incluindo a tabela de
referência cruzada e o marcador de fim de arquivo. Só então calcula os dois offsets reais,
grava-os de volta no placeholder sem deslocar nenhum byte, aplica hash aos dois
segmentos e coloca o objeto CMS resultante no espaço reservado. O
placeholder é preenchido com zeros até um comprimento constante justamente para que inserir os
números reais não desloque os bytes que serão submetidos ao hash. Essa é a única ordem que
produz uma assinatura autoconsistente. O engine trata qualquer falha nessa
sequência como erro fatal, não como fallback silencioso.
Para o perfil PDF 2.0, o próprio objeto de assinatura é uma estrutura CMS
SignedData destacada (detached). O dicionário do PDF diz onde e como; o objeto CMS
carrega o quem e a prova criptográfica.
- Step 1 of 4: ISO 32000-2 §12.8.1 — ByteRange digest & signature dictionary
- Step 2 of 4: ISO 32000-2 §12.8.3.3 — ETSI.CAdES.detached SubFilter
- Step 3 of 4: ETSI EN 319 142-1 PAdES baseline profile
- Step 4 of 4: RFC 5652 CMS SignedData in Contents
O que a evidência diz
Seção intitulada “O que a evidência diz” Evidence: Standard-backed O mecanismo é definido pela
Spec: ISO 32000-2, §12.8.1 ISO 32000-2 §12.8.1 . Um digest de byte range é
calculado sobre um intervalo de bytes indicado pela entrada ByteRange. Esse intervalo
deve ser o arquivo inteiro incluindo o dicionário de assinatura, mas excluindo
o valor da assinatura — a entrada Contents. ByteRange é um array de
pares de inteiros: offset inicial e comprimento. Intervalos descontíguos são
usados especificamente para permitir que o digest omita o próprio valor da assinatura.
Para o perfil PDF 2.0, a Spec: ISO 32000-2, §12.8.3.3 ISO 32000-2 §12.8.3.3 especifica que, quando o SubFilter é ETSI.CAdES.detached, o valor de Contents é um objeto CMS SignedData codificado em DER — a mesma estrutura definida pela
Spec: RFC 5652 RFC 5652 — e o perfil PAdES desse objeto
é o que a Spec: ETSI EN 319 142-1 ETSI EN 319 142-1 descreve.
O escopo não é igual para todas as assinaturas. A Spec: ISO 32000-2, §12.7.4.5 ISO 32000-2 §12.7.4.5 define a permissão MDP: um valor de 0 torna a assinatura uma assinatura de aprovação, enquanto os valores 1–3 a tornam uma assinatura de certificação que restringe quais modificações posteriores mantêm o documento em conformidade. O mecanismo de byte range é o mesmo; a promessa sobre o futuro é diferente.
O engine do NextPDF implementa exatamente isso: um placeholder ByteRange de largura fixa, o digest concatenado de dois segmentos e um objeto CMS destacado em um espaço Contents reservado, finalizado somente depois que o arquivo está completo.
Exemplo prático
Seção intitulada “Exemplo prático”Você raramente monta um ByteRange manualmente. O objetivo do exemplo é mostrar o formato do resultado para que você o reconheça ao inspecionar um arquivo assinado.
<?php
declare(strict_types=1);
use NextPDF\Security\Signature\ByteRangeCalculator;
// Offsets the engine knows only after the whole PDF is written:// $contentsStart — byte just before the '<' of the hex signature// $contentsEnd — byte just after the '>' that closes it// $fileLength — total file size in bytes$range = ByteRangeCalculator::calculate( contentsStart: $contentsStart, contentsEnd: $contentsEnd, fileLength: $fileLength,);// $range === [0, $contentsStart, $contentsEnd, $fileLength - $contentsEnd]// Segment 1: file start → just before the signature value// Segment 2: just after the signature value → end of file// The signature value itself is the gap. It is never hashed.
$signedMessage = ByteRangeCalculator::extractSignedData($pdfBytes, $range);// $signedMessage is segment 1 concatenated with segment 2 — exactly the// bytes the cryptographic digest is computed over.A lacuna entre os dois segmentos é o valor da assinatura. Ele não pode fazer parte do próprio digest; por isso, o intervalo tem duas partes, não uma.
Equívoco comum
Seção intitulada “Equívoco comum”A armadilha é acreditar que uma assinatura válida significa que o arquivo inteiro que você está vendo foi assinado. Não significa. Significa que os bytes dentro do intervalo declarado estão intactos. Uma revisão posterior pode legitimamente anexar conteúdo — uma segunda assinatura, dados de formulário, material de validação — fora desse intervalo. A primeira assinatura permanece válida e não diz nada sobre o acréscimo. Um visualizador correto informa que uma assinatura cobre “o documento tal como ele existia no momento da assinatura”, não “todo byte na tela”. Tratar os dois como a mesma coisa é o que permite que um documento assinado ganhe conteúdo não assinado com aparência de assinado.
Limites e fronteiras
Seção intitulada “Limites e fronteiras”Esta página explica a estrutura, não a confiança. Um
ByteRange corretamente formado e um objeto CMS informam que os bytes estão intactos e qual chave os
assinou. Eles não informam, por si só, se essa chave pertence a quem você
pensa, se o certificado dela era válido no momento da assinatura ou se ele foi posteriormente
revogado. Isso é trabalho de caminho de certificação e revogação, abordado em
Validar uma assinatura corretamente.
Esta página também não cobre quando a assinatura ocorreu com qualquer
autoridade independente. Um horário de assinatura autodeclarado não é horário confiável —
consulte Carimbos de tempo e horário confiável.
O NextPDF constrói a estrutura descrita aqui; os certificados, as âncoras de confiança
e a autoridade de carimbo de tempo são fornecidos pela sua implantação, não pelo engine.
O que o engine entrega, por tier, é a capacidade de construir a estrutura:
| Edition | Availability |
|---|---|
| Core | PAdES B-B: o dicionário de assinatura, o ByteRange de largura fixa e o objeto CMS SignedData destacado descrito nesta página. |
| Pro | Adiciona PAdES B-T — um carimbo de tempo confiável sobre o valor da assinatura — à mesma estrutura. |
| Enterprise | Adiciona os perfis de longo prazo (B-LT, B-LTA): material de validação embutido e carimbos de tempo de documento sobrepostos à mesma base de byte range. |
Documentação relacionada
Seção intitulada “Documentação relacionada”- Atualizações incrementais e por que elas importam — por que anexar, e não reescrever, é o que mantém o byte range da primeira assinatura intacto.
- Perfis baseline PAdES — o que é sobreposto a esta estrutura e qual perfil uma obrigação exige.
- Validação de longo prazo — como a evidência de validação é embutida para que uma assinatura permaneça verificável por anos.
Glossário
Seção intitulada “Glossário”- Dicionário de assinatura — o dicionário do PDF que identifica o handler de assinatura, o
SubFilter, oByteRangee o valor deContents. ByteRange— um array de pares de inteiros(offset, length)que declaram os bytes exatos que o digest da assinatura cobre.Contents— a entrada hexadecimal que contém o valor da assinatura (para PDF 2.0, um objeto CMSSignedDatadestacado); ela é excluída do próprio digest.- CMS
SignedData— estrutura Cryptographic Message Syntax (RFC 5652) que carrega o certificado do signatário e os bytes da assinatura. - PAdES — PDF Advanced Electronic Signatures: o perfil ETSI de assinaturas CMS para PDF, definido na série ETSI EN 319 142.
- Assinatura de aprovação — uma assinatura com a permissão
MDP0; ela atesta o conteúdo sem restringir alterações posteriores. - Assinatura de certificação — uma assinatura com uma permissão DocMDP (
MDP1–3) que limita quais modificações posteriores mantêm o documento em conformidade.