💡 Esempio completo funzionante disponibile su GitHub:
compare-encrypted-pdf-and-word-documents-dotnet

Il vecchio metodo era doloroso

Due revisioni di un accordo di fornitura arrivano nella tua casella di posta. Entrambe sono protette da password, ognuna con una password diversa, e qualcuno ha bisogno di una copia contrassegnata che mostri le modifiche. La libreria di confronto che utilizzi si aspetta input in chiaro, quindi il flusso di lavoro aggiunge un passaggio: decrittare entrambi i file in una cartella temporanea, confrontare le copie in chiaro, poi ricordarsi di cancellarle. Quella cartella temporanea è ora il punto più debole di un flusso di lavoro che esiste specificamente perché i documenti sono sensibili.

Esiste una seconda versione dello stesso problema, più facile da trascurare. Alcuni team evitano la cartella temporanea e decrittano direttamente in memoria, risolvendo la questione della pulizia ma non quella del formato: l’API di decrittazione varia a seconda del formato, quindi supportare fogli di calcolo crittografati dopo PDF crittografati significa una seconda integrazione anziché una seconda riga di codice.

Il costo non è principalmente nella chiamata di decrittazione – è in tutto ciò che la circonda. Le copie in chiaro devono essere scritte da qualche parte, pulite in ogni percorso di uscita, comprese le situazioni di errore, e tenute fuori da backup e dump di crash. Un diff prodotto in questo modo arriva anche non protetto per impostazione predefinita, così l’output di due input crittografati diventa il file unico nella catena che chiunque può aprire.

Il vero costo della deviazione di decrittazione: una directory temporanea che contiene copie in chiaro di documenti crittografati per un motivo, con una pulizia che deve essere corretta in ogni percorso di errore.

C’è un modo migliore

Il confronto protetto da password è una funzionalità di GroupDocs.Comparison per .NET che apre file PDF, DOCX, XLSX e PPTX crittografati in loco e decide quale password protegge il risultato del confronto. Nessun passaggio di decrittazione, nessun intermedio in chiaro: la password viaggia con il documento nel confronto stesso, come proprietà di LoadOptions.

Prima di iniziare, avrai bisogno di:

  • .NET 8.0 SDK o versioni successive
  • GroupDocs.Comparison 26.9.0 (licenza temporanea)
  • Due documenti crittografati dello stesso formato e le loro password

Installa con un solo comando:

dotnet add package GroupDocs.Comparison

Il nuovo modo: documenti crittografati direttamente nel Comparer

L’esempio qui sotto confronta due PDF crittografati – la sorgente si apre con 1234, la destinazione con 4321 – e scrive un unico file di risultato con le modifiche unite in linea. Password deliberatamente diverse, perché è lì che si nasconde il primo errore.

Passo 1 – Assegna a ogni documento il proprio LoadOptions

Un Comparer contiene una sorgente e un numero qualsiasi di destinazioni, e ogni documento porta la propria protezione. La password della sorgente va al costruttore; la password di ciascuna destinazione va alla sua chiamata Add.

// One LoadOptions per document - the constructor's options unlock the
// source only, and never reach the targets.
using var comparer = new Comparer("source.pdf",
    new LoadOptions { Password = "1234" });
comparer.Add("target.pdf", new LoadOptions { Password = "4321" });

Questo è il dettaglio che sorprende le persone. Passare un unico LoadOptions al costruttore e aspettarsi che copra le destinazioni è il modo più comune in cui le cose vanno storte, e a causa del momento in cui si verifica l’errore, non si annuncia dove guardare.

Passo 2 – Decidi cosa protegge il risultato

CompareOptions.PasswordSaveOption sceglie la protezione dell’output: None, Source, Target o User. L’impostazione predefinita è None, che trasforma silenziosamente due input crittografati in un risultato non protetto.

// Inline markup, and the result reuses the source document's password.
var options = new PdfCompareOptions
{
    DisplayMode = PdfCompareOptions.ComparisonDisplayMode.Inline,
    PasswordSaveOption = PasswordSaveOption.Source
};

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

Punti chiave:

  • PasswordSaveOption: Source riutilizza la password della sorgente sull’output. Scegli User con SaveOptions.Password per impostare una nuova password.
  • ComparisonDisplayMode: annidato dentro PdfCompareOptions, che offre anche SideBySide e Interleaved. WordCompareOptions dichiara il proprio enum con lo stesso nome ma valori diversi, quindi il nome semplice non compilerà – qualificalo.

Passo 3 – Proteggi l’output con una password propria

Quando il diff arriva ai revisori che non dovrebbero conoscere nessuna delle password originali, PasswordSaveOption.User prende il valore da SaveOptions.Password invece di riutilizzare una password di input.

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);

Entrambi gli oggetti vanno alla sovraccarico Compare a tre argomenti. Impostare SaveOptions.Password da solo non cambia nulla – è il valore enum a attivare la password lato salvataggio. Il risultato di questa chiamata si apre con 5678 e rifiuta 1234.

Perché il mio try/catch intorno al Comparer non intercetta una password errata?

Perché il costruttore non apre mai il documento. Registra solo il percorso, così come fa Add. Entrambi i documenti vengono letti quando viene eseguito Compare, ed è lì che viene lanciata PasswordProtectedFileException con il messaggio Password is missing. Una password sbagliata si comporta identicamente: viene accettata in silenzio al momento della costruzione, poi rifiutata più tardi in Compare.

Quindi proteggi la chiamata di confronto, non il costruttore. L’ho scoperto nel modo più lento, avvolgendo la costruzione in un try e osservando un file crittografato passare direttamente attraverso di esso prima di fallire tre righe più tardi. Il repository stampa ogni fase, rendendo l’ordine evidente a una prima lettura:

using var comparer = new Comparer("source.pdf");   // succeeds
comparer.Add("target.pdf");                        // succeeds
comparer.Compare("Result/unreachable.pdf");        // throws here

Fianco a fianco: Prima vs. Dopo

Prima (decrittare prima) Dopo (GroupDocs.Comparison)
Passaggi del pipeline Decrittare entrambi, confrontare, cancellare copie temporanee Confrontare
Testo in chiaro su disco Due copie, pulizia in ogni percorso di errore Nessuna
Protezione del risultato Un passaggio separato di ri‑crittazione Un valore PasswordSaveOption
Copertura dei formati Strumenti di decrittazione per formato Un LoadOptions.Password per PDF, DOCX, XLSX, PPTX
Codice richiesto Helper di decrittazione più confronto 4 righe

Le funzionalità di confronto non cambiano con input crittografati. Le modalità di visualizzazione, le pagine di riepilogo e il rilevamento dello stile si comportano esattamente come per i file in chiaro, perché la protezione è gestita interamente nello strato di caricamento.

Questa stratificazione è ciò che rende la copertura dei formati economica. LoadOptions.Password è una semplice proprietà string, e la stessa proprietà sblocca PDF, DOCX, XLSX e PPTX – il codice di caricamento nell’esempio Word più avanti è carattere per carattere quello usato negli esempi PDF. Cambia solo la classe delle opzioni, e solo perché ogni formato espone scelte di rendering diverse. Aggiungere il supporto a fogli di calcolo crittografati a un codice che già confronta PDF crittografati non costa nulla nel percorso di caricamento.

Esempio reale: Redlining di contratti tra studi legali

Un team legale riceve ogni revisione di un accordo crittografata, con la password ruotata ad ogni scambio così che una password trapelata non esponga l’intera cronologia. Il partner revisore ha bisogno di un documento contrassegnato per ogni turno, e secondo le regole di conservazione la copia contrassegnata non può rimanere non protetta su una condivisione di file.

Due impostazioni lo coprono. Ogni documento è sbloccato dal proprio LoadOptions, così la rotazione delle password non richiede gestione speciale, e PasswordSaveOption.User assegna a ogni diff distribuito una password propria – una che sblocca il confronto e nient’altro.

// Word revisions, so the reviewing partner can accept or reject each edit.
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);

Cos’altro puoi fare con GroupDocs.Comparison?

  • Confrontare più di due documenti protetti: aggiungi diversi target crittografati a un unico confronto, per formati Word e presentazione.
  • Generare revisioni native di Word: WordCompareOptions.ComparisonDisplayMode.Revisions scrive le modifiche che un revisore può accettare o rifiutare direttamente in Word.
  • Controllare il caricamento di risorse esterne: blocca o consenti nella whitelist i riferimenti remoti che un documento contiene, un’ulteriore salvaguardia di LoadOptions.
  • Generare una pagina di riepilogo: GenerateSummaryPage aggiunge una panoramica delle modifiche al documento di risultato.

Conclusione

La deviazione di decrittazione non riguardava il confronto – riguardava una libreria che non poteva leggere ciò che avevi. Impostare LoadOptions.Password per documento elimina la cartella temporanea, i percorsi di pulizia e il diff non protetto alla fine della catena. Rimangono tre decisioni: una password per documento, un PasswordSaveOption esplicito anziché il valore predefinito None, e la gestione degli errori intorno a Compare dove il fallimento avviene realmente.

Pronto a automatizzare il tuo flusso di lavoro documentale?

Risorse aggiuntive