💡 Exemplo completo disponível no GitHub:
compare-encrypted-pdf-and-word-documents-dotnet

O Antigo Método Era Doloroso

Duas revisões de um contrato de fornecimento chegam à sua caixa de entrada. Ambas estão protegidas por senha, cada uma com uma senha diferente, e alguém precisa de uma cópia anotada mostrando o que mudou. A biblioteca de comparação que você tem espera entrada em texto simples, então o pipeline ganha um passo: descriptografar ambos os arquivos para uma pasta temporária, comparar as cópias em texto simples e, então, lembrar de excluí‑las. Essa pasta temporária agora é o elo mais fraco em um fluxo de trabalho que existe especificamente porque os documentos são sensíveis.

Existe uma segunda versão do mesmo problema que é mais fácil de perder. Algumas equipes pulam a pasta temporária e descriptografam na memória, o que resolve a questão da limpeza, mas não a questão do formato: a API de descriptografia difere por formato, de modo que dar suporte a planilhas criptografadas após PDFs criptografados significa uma segunda integração em vez de uma segunda linha de código.

O custo não está principalmente na chamada de descriptografia – está em tudo ao redor dela. Cópias em texto simples precisam ser gravadas em algum lugar, limpas em cada caminho de saída, inclusive nos de falha, e mantidas fora de backups e dumps de falha. Um diff produzido dessa forma também chega desprotegido por padrão, de modo que a saída de duas entradas criptografadas se torna o único arquivo na cadeia que qualquer pessoa pode abrir.

O custo real do desvio de descriptografia: um diretório temporário contendo cópias em texto simples de documentos que foram criptografados por um motivo, com limpeza que precisa ser correta em cada caminho de erro.

Existe um Caminho Melhor

A comparação protegida por senha é um recurso do GroupDocs.Comparison para .NET que abre arquivos PDF, DOCX, XLSX e PPTX criptografados no local e decide qual senha protege o resultado da comparação. Sem etapa de descriptografia, sem intermediários em texto simples: a senha viaja com o documento para a própria comparação, como uma propriedade em LoadOptions.

Antes de começar, você precisará de:

  • .NET 8.0 SDK ou posterior
  • GroupDocs.Comparison 26.9.0 (licença temporária)
  • Dois documentos criptografados do mesmo formato e suas senhas

Instale com um único comando:

dotnet add package GroupDocs.Comparison

O Novo Caminho: Documentos Criptografados Direto no Comparador

O exemplo abaixo compara dois PDFs criptografados – a origem abre com 1234, o destino com 4321 – e grava um único arquivo de resultado com as alterações mescladas inline. Senhas deliberadamente diferentes, porque é aí que o primeiro erro se esconde.

Etapa 1 – Dê a cada documento seu próprio LoadOptions

Um Comparer contém uma origem e qualquer número de destinos, e cada documento carrega sua própria proteção. A senha da origem vai para o construtor; a senha de cada destino vai para sua própria chamada Add.

// Um LoadOptions por documento – as opções do construtor desbloqueiam
// apenas a origem e nunca chegam aos destinos.
using var comparer = new Comparer("source.pdf",
    new LoadOptions { Password = "1234" });
comparer.Add("target.pdf", new LoadOptions { Password = "4321" });

Esse é o detalhe que pega as pessoas desprevenidas. Passar um único LoadOptions para o construtor e esperar que ele cubra os destinos é a forma mais comum de erro, e, devido ao momento em que a falha ocorre, ele não se anuncia onde você procuraria.

Etapa 2 – Decida o que protege o resultado

CompareOptions.PasswordSaveOption escolhe a proteção da saída: None, Source, Target ou User. O padrão é None, que silenciosamente transforma duas entradas criptografadas em um resultado desprotegido.

// Marcação inline, e o resultado reutiliza a senha do documento de origem.
var options = new PdfCompareOptions
{
    DisplayMode = PdfCompareOptions.ComparisonDisplayMode.Inline,
    PasswordSaveOption = PasswordSaveOption.Source
};

comparer.Compare("Result/1-pdf-inline.pdf", options);

Pontos chave:

  • PasswordSaveOption: Source reutiliza a senha da origem na saída. Escolha User com SaveOptions.Password para definir uma nova senha.
  • ComparisonDisplayMode: aninhado dentro de PdfCompareOptions, que também oferece SideBySide e Interleaved. WordCompareOptions declara seu próprio enum com o mesmo nome e valores diferentes, portanto o nome simples não compilará – qualifique‑o.

Etapa 3 – Proteja a saída com uma senha própria

Quando o diff vai para revisores que não devem possuir nenhuma das senhas originais, PasswordSaveOption.User usa o valor de SaveOptions.Password em vez de reutilizar a senha de entrada.

var compareOptions = new PdfCompareOptions
{
    DisplayMode = PdfCompareOptions.ComparisonDisplayMode.Inline,
    PasswordSaveOption = PasswordSaveOption.User
};
var saveOptions = new SaveOptions { Password = "5678" };

comparer.Compare("Result/4-own-password.pdf", saveOptions, compareOptions);

Ambos os objetos vão para a sobrecarga de Compare com três argumentos. Definir SaveOptions.Password isoladamente não altera nada – o valor do enum é que ativa a senha do lado de salvamento. O resultado desta chamada abre com 5678 e rejeita 1234.

Por que meu try/catch ao redor do Comparer não captura uma senha errada?

Porque o construtor nunca abre o documento. Ele registra o caminho, e o mesmo acontece com Add. Ambos os documentos são lidos quando Compare é executado, e é aí que PasswordProtectedFileException com a mensagem Password is missing é lançada. Uma senha errada se comporta de forma idêntica: é aceita silenciosamente no momento da construção e, depois, rejeitada em Compare.

Portanto, proteja a chamada de comparação, não o construtor. Descobri isso da maneira lenta, envolvendo a construção em um try e observando um arquivo criptografado passar direto antes de falhar três linhas depois. O repositório imprime cada estágio, o que torna a ordem óbvia na primeira leitura:

using var comparer = new Comparer("source.pdf");   // tem sucesso
comparer.Add("target.pdf");                        // tem sucesso
comparer.Compare("Result/unreachable.pdf");        // lança aqui

Lado a Lado: Antes vs. Depois

Antes (descriptografar primeiro) Depois (GroupDocs.Comparison)
Etapas do pipeline Descriptografar ambos, comparar, excluir cópias temporárias Comparar
Texto simples no disco Duas cópias, limpeza em cada caminho de erro Nenhuma
Proteção do resultado Etapa separada de re‑criptografia Um valor PasswordSaveOption
Cobertura de formatos Ferramentas de descriptografia por formato Um LoadOptions.Password para PDF, DOCX, XLSX, PPTX
Código necessário Helper de descriptografia + comparação 4 linhas

Os recursos de comparação não mudam com entrada criptografada. Modos de exibição, páginas de resumo e detecção de estilo comportam‑se exatamente como fazem para arquivos em texto simples, porque a proteção é tratada integralmente na camada de carregamento.

Essa camada é o que torna a cobertura de formatos barata. LoadOptions.Password é uma propriedade string simples, e a mesma propriedade desbloqueia PDF, DOCX, XLSX e PPTX – o código de carregamento no exemplo de Word mais abaixo é literalmente o mesmo que o dos exemplos de PDF. Apenas a classe de opções muda, e só porque cada formato expõe escolhas de renderização diferentes. Adicionar suporte a planilhas criptografadas a um código que já compara PDFs criptografados não custa nada no caminho de carregamento.

Exemplo Real: Redação de Contrato Entre Escritórios de Advocacia

Uma equipe jurídica recebe cada revisão de um contrato criptografada, com a senha rotacionada a cada troca para que uma senha vazada não exponha todo o histórico. O sócio revisor precisa de um documento anotado por rodada, e, segundo as regras de retenção, a cópia anotada não pode ficar desprotegida em um compartilhamento de arquivos.

Dois ajustes cobrem isso. Cada documento é desbloqueado por seu próprio LoadOptions, de modo que a rotação de senhas não requer tratamento especial, e PasswordSaveOption.User dá a cada diff distribuído uma senha própria – uma que desbloqueia a comparação e nada mais.

// Revisões de Word, para que o sócio revisor possa aceitar ou rejeitar cada edição.
var options = new WordCompareOptions
{
    DisplayMode = WordCompareOptions.ComparisonDisplayMode.Revisions,
    PasswordSaveOption = PasswordSaveOption.Source
};

using var comparer = new Comparer("round3.docx",
    new LoadOptions { Password = "1234" });
comparer.Add("round4.docx", new LoadOptions { Password = "4321" });
comparer.Compare("Result/redline.docx", options);

O Que Mais Você Pode Fazer com GroupDocs.Comparison?

  • Comparar mais de dois documentos protegidos: adicione vários alvos criptografados a uma única comparação, para formatos Word e de apresentação.
  • Gerar revisões nativas do Word: WordCompareOptions.ComparisonDisplayMode.Revisions grava alterações que o revisor aceita ou rejeita diretamente no Word.
  • Controlar o carregamento de recursos externos: bloqueie ou permita listas brancas de referências remotas que um documento contém, outra salvaguarda via LoadOptions.
  • Gerar uma página de resumo: GenerateSummaryPage adiciona uma visão geral das mudanças ao documento de resultado.

Conclusão

O desvio de descriptografia nunca foi sobre a comparação – era sobre uma biblioteca que não conseguia ler o que você tinha. Definir LoadOptions.Password por documento elimina a pasta temporária, os caminhos de limpeza e o diff desprotegido ao final da cadeia. Restam apenas três decisões: uma senha por documento, um PasswordSaveOption explícito em vez do padrão None, e tratamento de erro ao redor de Compare, onde a falha realmente ocorre.

Pronto para automatizar seu fluxo de trabalho de documentos?

Recursos Adicionais