💡 Volledig werkend voorbeeld beschikbaar op GitHub:
compare-encrypted-pdf-and-word-documents-dotnet
De oude manier was pijnlijk
Twee revisies van een leveringscontract belanden in je inbox. Beide zijn met een wachtwoord beveiligd, elk met een ander wachtwoord, en iemand heeft een gemarkeerde kopie nodig waarin staat wat er is veranderd. De vergelijkingsbibliotheek die je hebt, verwacht platte tekst als invoer, dus de pijplijn krijgt een extra stap: beide bestanden ontsleutelen naar een tijdelijke map, de platte‑tekstkopieën vergelijken, en vervolgens onthouden ze te verwijderen. Die tijdelijke map is nu de zwakste schakel in een workflow die specifiek bestaat omdat de documenten gevoelig zijn.
Er is een tweede versie van hetzelfde probleem die makkelijker over het hoofd wordt gezien. Sommige teams slaan de tijdelijke map over en ontsleutelen in het geheugen, wat de opruimvraag oplost maar niet de formaatvraag: de ontsleutel‑API verschilt per formaat, dus ondersteuning voor versleutelde spreadsheets na versleutelde PDF‑bestanden betekent een tweede integratie in plaats van een tweede regel code.
De kosten liggen niet vooral in de ontsleutel‑aanroep – ze zitten in alles eromheen. Platte‑tekstkopieën moeten ergens worden weggeschreven, bij elke exit‑pad (inclusief de foutpaden) worden opgeschoond, en buiten back‑ups en crash‑dumps worden gehouden. Een diff die op die manier wordt geproduceerd, komt standaard onbeschermd, zodat de output van twee versleutelde invoeren het ene bestand in de keten wordt dat iedereen kan openen.
De werkelijke kosten van de ontsleutel‑omweg: een tijdelijke map die platte‑tekstkopieën van documenten bevat die om een reden versleuteld waren, met een opruiming die op elk foutpad correct moet zijn.
Er is een betere manier
Wachtwoord‑beveiligde vergelijking is een GroupDocs.Comparison‑functionaliteit voor .NET die versleutelde PDF‑, DOCX‑, XLSX‑ en PPTX‑bestanden ter plaatse opent en beslist welk wachtwoord de vergelijkingsresultaat beschermt. Geen ontsleutelstap, geen platte‑tekstintermediairs: het wachtwoord reist mee met het document naar de vergelijking zelf, als een eigenschap op LoadOptions.
Voordat we beginnen, heb je nodig:
- .NET 8.0 SDK of later
- GroupDocs.Comparison 26.9.0 (temporary licence)
- Twee versleutelde documenten van hetzelfde formaat, en hun wachtwoorden
Installeer met één commando:
dotnet add package GroupDocs.Comparison
De nieuwe manier: versleutelde documenten direct in de comparer
Het voorbeeld hieronder vergelijkt twee versleutelde PDF‑bestanden – de bron opent met 1234, het doel met 4321 – en schrijft één resultaatbestand met de wijzigingen inline samengevoegd. Opzettelijk verschillende wachtwoorden, want daar verbergt zich de eerste fout.
Stap 1 – Geef elk document zijn eigen LoadOptions
Een Comparer bevat één bron en een willekeurig aantal doelen, en elk document draagt zijn eigen bescherming. Het wachtwoord van de bron gaat naar de constructor; het wachtwoord van elk doel gaat naar zijn eigen Add‑aanroep.
// 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" });
Dit is het detail dat mensen in de val lokt. Een enkele LoadOptions naar de constructor doorgeven en verwachten dat die de doelen dekt, is de meest voorkomende fout, en omdat de fout op een bepaald moment optreedt, kondigt hij zich niet aan op de plek waar je zou zoeken.
Stap 2 – Bepaal wat het resultaat beschermt
CompareOptions.PasswordSaveOption kiest de bescherming van de output: None, Source, Target of User. Standaard is None, wat twee versleutelde invoeren stilletjes omzet in één onbeschermd resultaat.
// 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);
Belangrijke punten:
- PasswordSaveOption:
Sourcehergebruikt het bronwachtwoord voor de output. KiesUsermetSaveOptions.Passwordom een nieuw wachtwoord uit te geven. - ComparisonDisplayMode: genest binnen
PdfCompareOptions, die ookSideBySideenInterleavedbiedt.WordCompareOptionsdeclareert zijn eigen enum met dezelfde naam maar andere waarden, dus de kale naam zal niet compileren – kwalificeer hem.
Stap 3 – Bescherm de output met een eigen wachtwoord
Wanneer de diff naar beoordelaars gaat die geen van beide oorspronkelijke wachtwoorden mogen kennen, neemt PasswordSaveOption.User de waarde van SaveOptions.Password in plaats van een invoer‑wachtwoord te hergebruiken.
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);
Beide objecten gaan naar de overload van Compare met drie argumenten. Alleen SaveOptions.Password instellen verandert niets – de enum‑waarde activeert het wachtwoord aan de opslaan‑kant. Het resultaat van deze aanroep opent met 5678 en wijst 1234 af.
Waarom vangt mijn try/catch rond de Comparer geen fout wachtwoord?
Omdat de constructor het document nooit opent. Hij registreert alleen het pad, en dat doet Add ook. Beide documenten worden gelezen wanneer Compare wordt uitgevoerd, en daar wordt PasswordProtectedFileException met de boodschap Password is missing gegooid. Een verkeerd wachtwoord gedraagt zich identiek: het wordt stilletjes geaccepteerd tijdens de constructie, en later bij Compare afgewezen.
Dus bescherm de vergelijkingsaanroep, niet de constructor. Ik ontdekte dit op de langzame manier, door de constructie in een try te wikkelen en een versleuteld bestand recht doorheen te laten gaan voordat het drie regels later faalt. Het repository print elke fase, waardoor de volgorde duidelijk wordt bij een eerste lezing:
using var comparer = new Comparer("source.pdf"); // succeeds
comparer.Add("target.pdf"); // succeeds
comparer.Compare("Result/unreachable.pdf"); // throws here
Zij‑aan‑zij: vóór vs. daarna
| Voor (eerst ontsleutelen) | Na (GroupDocs.Comparison) | |
|---|---|---|
| Pijplijnstappen | Beide ontsleutelen, vergelijken, tijdelijke kopieën verwijderen | Vergelijken |
| Platte tekst op schijf | Twee kopieën, opruimen bij elk foutpad | Geen |
| Resultaatbescherming | Een aparte her‑versleutelstap | Eén PasswordSaveOption‑waarde |
| Formaatdekking | Per‑formaat ontsleutel‑tools | Eén LoadOptions.Password voor PDF, DOCX, XLSX, PPTX |
| Benodigde code | Ontsleutel‑helper plus vergelijking | 4 regels |
De vergelijkingsfuncties veranderen niet bij versleutelde invoer. Weergavemodi, samenvattingspagina’s en stijldetectie gedragen zich precies zoals bij platte‑tekstbestanden, omdat bescherming volledig in de laaglayer wordt afgehandeld.
Die laagstructuur maakt de formaatdekking goedkoop. LoadOptions.Password is een eenvoudige string‑eigenschap, en dezelfde eigenschap ontgrendelt PDF, DOCX, XLSX en PPTX – de laadcode in het Word‑voorbeeld verderop is karakter‑voor‑karakter wat de PDF‑voorbeelden gebruiken. Alleen de opties‑klasse verandert, en alleen omdat elk formaat andere renderingskeuzes biedt. Het toevoegen van ondersteuning voor versleutelde spreadsheets aan code die al versleutelde PDF’s vergelijkt, kost niets in het laadpad.
Praktijkvoorbeeld: contract‑redlining tussen advocatenkantoren
Een juridisch team ontvangt elke revisie van een overeenkomst versleuteld, met het wachtwoord per uitwisseling geroteerd zodat een gelekt wachtwoord niet de volledige geschiedenis blootlegt. De beoordelende partner heeft per ronde één gemarkeerd document nodig, en volgens bewaarbeleid mag de gemarkeerde kopie niet onbeschermd op een gedeelde map staan.
Twee instellingen dekken dit. Elk document wordt ontgrendeld met zijn eigen LoadOptions, dus het roteren van wachtwoorden vereist geen speciale behandeling, en PasswordSaveOption.User geeft elke verspreide diff een eigen wachtwoord – één dat de vergelijking ontgrendelt en niets anders.
// 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);
Wat kun je nog meer doen met GroupDocs.Comparison?
- Meer dan twee beschermde documenten vergelijken: voeg meerdere versleutelde doelen toe aan één vergelijking, voor Word‑ en presentatieformaten.
- Native Word‑revisies produceren:
WordCompareOptions.ComparisonDisplayMode.Revisionsschrijft wijzigingen die een beoordelaar in Word zelf kan accepteren of afwijzen. - Externe resource‑laden beheersen: blokkeer of whitelist de externe verwijzingen die een document bevat, een extra
LoadOptions‑bescherming. - Een samenvattingspagina genereren:
GenerateSummaryPagevoegt een overzicht van wijzigingen toe aan het resultaatdocument.
Conclusie
De ontsleutel‑omweg ging nooit over de vergelijking – het ging over een bibliotheek die niet kon lezen wat jij had. Het instellen van LoadOptions.Password per document verwijdert de tijdelijke map, de opruim‑paden en de onbeschermde diff aan het einde van de keten. Drie beslissingen zijn dan alles wat overblijft: een wachtwoord per document, een expliciete PasswordSaveOption in plaats van de standaard None, en foutafhandeling rond Compare waar de fout daadwerkelijk optreedt.
Klaar om je document‑workflow te automatiseren?
- Probeer de gratis API‑trial
- Verken het laden van wachtwoord‑beveiligde documenten
- Lees de volledige vergelijkingsgids voor beschermde documenten
- Bekijk het voorbeeldproject op GitHub