Artigo Técnico

HotPDF: AES-256 encryption and permission policy in Delphi

Uma flag de permissão de um PDF não é um bloqueio. É apenas um pedido que o arquivo faz ao programa que o abre, o qual o visualizador é livre de ignorar. Este fato único determina a forma como deve analisar todas as outras opções nesta página. A confidencialidade real provém de um único local: a encriptação AES-256 baseada numa palavra-passe que o leitor do arquivo não possui. Tudo o resto, como as caixas de verificação "sem impressão" e "sem cópia", são políticas que o software compatível aceita respeitar e o software hostil ignora. Se misturar estas duas camadas, arrisca-se a distribuir algo que parece seguro numa demonstração mas que falha em ambiente de produção

O HotPDF é um componente PDF nativo VCL para Delphi e C++Builder que expõe o modelo de proteção da norma ISO 32000 através de um pequeno conjunto de propriedades. As propriedades são simples de configurar. A dificuldade consiste em saber qual delas lhe garante proteção criptográfica real e qual representa apenas uma sugestão de cortesia, bem como em definir a ordem correta de atribuição para que a encriptação solicitada seja efetivamente aplicada

O que as duas palavras-passe realmente garantem

A encriptação de PDF define duas credenciais com funções distintas, e confundi-las é o erro de design mais comum em códigos de geração protegida. A palavra-passe do usuário (user password) controla a desencriptação. Sem ela, ou sem a palavra-passe do proprietário, um leitor compatível não consegue reconstruir a chave do arquivo e o conteúdo permanece criptograficamente ilegível. Por outro lado, a palavra-passe do proprietário (owner password) controla as definições de permissão: um leitor que receba a palavra-passe do proprietário obtém acesso total, independentemente do que digam as flags de restrição

Os bits de permissão assentam em bases menos sólidas. Impressão, extração de conteúdo, preenchimento de formulários: cada um destes itens é apenas uma flag que o visualizador lê e decide respeitar (ISO 32000-2 §7.6.4). A encriptação protege os bytes. As flags de permissão apenas instruem o software compatível, e fazem-no após o acesso. Qualquer usuário que abra o documento com a palavra-passe do usuário já possui o conteúdo desencriptado em memória, pelo que as restrições de "cópia proibida" ou "impressão proibida" têm significado para um visualizador bem comportado, mas nenhum para um usuário determinado. Construa o seu modelo de ameaças com base nesta premissa. A confidencialidade reside na palavra-passe do usuário. As permissões apenas moldam o que os visualizadores comuns oferecem, e nada mais

Ordem de configuração: tudo antes de BeginDoc

O HotPDF constrói o dicionário de encriptação e gera a chave do arquivo no momento em que o BeginDoc é executado. O estado das propriedades de proteção nesse instante é o que determina as definições do documento, e alterá-las posteriormente não surtirá qualquer efeito. A propriedade mais importante neste contexto é a CryptKeyLength, que seleciona o esquema a partir dos valores THPDFKeyType (k40, k128, aes128 e aes256). Se a atribuir após o BeginDoc, não receberá qualquer exceção ou aviso, mas sim um arquivo que manteve silenciosamente as configurações iniciais. Este tipo de divergência silenciosa é o pior cenário: passa em todos os testes locais e manifesta-se meses mais tarde como uma falha de conformidade no ambiente de produção do cliente

As palavras-passe são em UTF-8 e estão limitadas a 127 bytes, que é o limite da norma ISO 32000-2 para os esquemas AES-256. Se a sua política de segurança fornecer credenciais mais longas, efetue o corte do seu lado, onde pode controlar exatamente onde a truncatura ocorre. Se deixar isso ao acaso, a biblioteca e o visualizador podem divergir quanto ao ponto de corte, originando um arquivo que se abre no seu ambiente mas recusa a mesma palavra-passe noutros sistemas

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'statement.pdf';
    Pdf.ActivateProtection := True;
    Pdf.CryptKeyLength := aes256;        // must be set before BeginDoc
    Pdf.UserPassword := 'open-secret';
    Pdf.OwnerPassword := 'admin-secret';
    Pdf.UseAES256R6 := False;            // R=5: widest viewer support
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 720, 0, 'Account statement, June 2026');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Revisão 5 ou revisão 6: um boolean, dois ecossistemas

A propriedade UseAES256R6 seleciona o processo de validação AES-256, e esta escolha é mais complexa do que o seu tipo boolean sugere. Se a mantiver a False, o HotPDF aplica a revisão 5, o esquema AES-256 introduzido como extensão do PDF 1.7 e suportado por visualizadores com mais de quinze anos. Se a definir a True, obterá a revisão 6, a derivação de chave reforçada e normalizada na ISO 32000-2 para PDF 2.0, que corrige uma vulnerabilidade conhecida na validação de palavra-passe da revisão 5

Desta forma, a revisão 6 é a melhor opção em termos criptográficos. Contudo, é também a que pode causar incompatibilidades. Um arquivo com revisão 6 requer um visualizador compatível com PDF 1.7 Extension Level 3 ou PDF 2.0, e muito software em utilização não preenche estes requisitos: arquivos de gestão documental, renderizadores integrados noutros produtos e ferramentas antigas de fluxos de trabalho. Estes sistemas rejeitarão o arquivo no computador do cliente, mesmo que no seu ambiente de desenvolvimento funcione. O padrão prático recomendado é, por isso, a revisão 5. Utilize a revisão 6 apenas quando uma política de segurança especificar expressamente a ISO 32000-2 por revisão, e após confirmar que todos os destinatários a conseguem ler. Em qualquer caso, documente a sua escolha no código para referência futura

Os tipos de chave mais antigos merecem uma breve menção apenas para que saiba que os deve evitar. A enumeração THPDFKeyType ainda inclui k40, k128 e aes128, mas estes servem apenas para compatibilidade com arquivos históricos, e não para proteger documentos novos. O algoritmo RC4 de 40 bits é facilmente quebrado por hardware comum, e os esquemas de 128 bits são obsoletos face aos padrões AES-256 exigidos em qualquer auditoria de segurança atual. Para novos documentos, a única decisão relevante é entre a revisão 5 e a revisão 6; se ponderar utilizar os tipos antigos num novo projeto, algum requisito do fluxo de trabalho está incorreto

Flags de permissão sem palavra-passe de abertura

Frequentemente, o requisito é o oposto do sigilo. Qualquer pessoa deve poder ler o documento, mas a impressão ou a extração de conteúdo devem ser limitadas. Isto configura-se definindo a palavra-passe do usuário como vazia e especificando uma palavra-passe do proprietário, o que o PDF designa por modo de palavra-passe aberta (open-password mode), listando depois as ações permitidas em ProtectOptions

O conjunto THPDFProtectOptions mapeia diretamente os bits de permissão da norma ISO: prPrint e prPrint12bit para impressão em alta resolução, prInformationCopy para cópia e extração geral, prExtractContent para extração por tecnologias de apoio (como leitores de tela), além de prModifyStructure, prEditAnnotations, prFillAnnotations e prAssemble. Duas destas opções merecem especial atenção. Mantenha a prExtractContent ativa em quase todos os perfis. É o bit de que um leitor de tela necessita para aceder ao texto; desativá-lo converte uma opção de direitos numa barreira de acessibilidade que afetará usuários com deficiência sem que se aperceba. A outra rasteira é a flag prPrint isolada, sem a prPrint12bit: vários visualizadores reagem reduzindo a qualidade de impressão, o que os usuários reportarão como um erro de renderização e não como a definição de permissão que efetivamente é

A verificação demora apenas alguns minutos e deve constar da sua lista de verificação de lançamentos. Abra uma amostra de cada perfil no Acrobat, aceda às Propriedades do Documento e consulte o painel de Segurança, que discrimina o algoritmo ("AES de 256 bits") e lista as operações permitidas. Depois, abra o mesmo arquivo no visualizador mais antigo que os seus clientes utilizem. Este segundo teste é uma salvaguarda simples para evitar que um arquivo de revisão 6 passe nos testes de desenvolvimento e falhe no cliente que não atualizou o sistema

Pdf.ActivateProtection := True;
Pdf.CryptKeyLength := aes256;
Pdf.UserPassword := '';                      // anyone can open the file
Pdf.OwnerPassword := 'rotate-me-quarterly';  // guards the permission set
Pdf.ProtectOptions := [prPrint, prPrint12bit, prExtractContent];
Pdf.BeginDoc;
// ... page content ...
Pdf.EndDoc;

Remover a proteção de arquivos existentes

O processo de desencriptação utiliza o mesmo modelo de propriedades de forma inversa. Carregue o documento com uma credencial válida, desative a proteção e grave o resultado sem encriptação

Este método carrega todo o documento em memória, o que é adequado para arquivos comuns mas ineficiente para arquivos de grande dimensão. Quando o arquivo atinge centenas de megabytes, a função DecryptFile é a melhor alternativa: realiza a desencriptação durante uma cópia a nível de arquivo, utilizando um caminho de reescrita direta AES-256 que evita a criação da árvore completa de objetos. Esta função faz parte da API Direct File, detalhada no artigo de acompanhamento sobre processamento de PDFs de grande dimensão no Delphi

var
  Pdf: THotPDF;
  PageCount: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    PageCount := Pdf.LoadFromFile('encrypted.pdf', 'open-secret');
    if PageCount > 0 then
    begin
      Pdf.ActivateProtection := False;   // drop encryption on save
      Pdf.SaveLoadedDocument('plain.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Restrições relacionadas com a encriptação

Existem duas restrições importantes que deve conhecer antes de projetar sistemas com encriptação. A primeira prende-se com a conformidade de arquivo. A norma ISO 19005 proíbe expressamente a encriptação em conformidades PDF/A, pelo que qualquer fluxo que encripte um documento e declare simultaneamente a conformidade PDF/A é inconsistente; o HotPDF impede a ativação de ambas as opções no mesmo arquivo. Se necessitar de ambas, a solução passa por gerar dois arquivos: uma versão encriptada para distribuição e outra sem encriptação para arquivo

O segundo limite é incontornável. A encriptação de PDF não disponibiliza mecanismos de recuperação de credenciais. Se perder a palavra-passe do usuário num arquivo R5 ou R6, as únicas alternativas são a força bruta ou desistir do acesso. Por isso, gira as palavras-passe do proprietário e do usuário como geriria qualquer credencial de produção: crie-as dinamicamente, guarde-as num cofre digital e proceda à sua rotação periódica. A prática a evitar terminantemente é a inclusão destas chaves como constantes no código, sob pena de serem registadas no sistema de controle de versões e ficarem expostas na área de trabalho de todos os programadores

Uma última boa prática para o seu dia a dia: alterar a proteção de um arquivo que não foi criado por si utiliza o mesmo mecanismo da desencriptação, e não uma funcionalidade distinta. Carregue-o com a palavra-passe respetiva através de LoadFromFile, edite as ProtectOptions ou as palavras-passe e grave-o novamente com SaveLoadedDocument. Se possui autorização para desencriptar um arquivo, também a possui para alterar as suas permissões, sendo a estrutura do código idêntica à apresentada anteriormente

As propriedades de proteção aqui demonstradas fazem parte do Componente HotPDF padrão para Delphi e C++Builder; a página do produto inclui a referência de encriptação completa, com a enumeração detalhada de todas as permissões