Password-protect a PDF with AES-256 in C#
Setting a user password on a PdfDocument is enough to encrypt it: SelectPdf.Universal uses AES with a 256-bit key (revision 6 of the PDF security handler) by default. An owner password adds permissions such as no printing or no copying.
Compiled and run: Linux ARM64 · Linux x64 · macOS ARM64 · Windows x64
Build it step by step.
-
1Install the NuGet packages
SelectPdf.Universalis the library. The native package carries the Chromium engine for one platform: add the one for the platform you run on.dotnet add package SelectPdf.Universal dotnet add package SelectPdf.Universal.Native.win-x64
dotnet add package SelectPdf.Universal dotnet add package SelectPdf.Universal.Native.linux-x64
dotnet add package SelectPdf.Universal dotnet add package SelectPdf.Universal.Native.linux-arm64
dotnet add package SelectPdf.Universal dotnet add package SelectPdf.Universal.Native.osx-arm64
-
2Create a console app
Any .NET 8 or .NET 10 project works. To follow along, create a console app, run the install commands above inside its folder, then replace
Program.cswith the code below.dotnet new console -n PdfSample cd PdfSample
-
3Import the namespace
using SelectPdf.Universal;
HtmlToPdf,PdfDocumentand the encryption enums are all inSelectPdf.Universal. -
4Get the password
// 1. The password readers type to open the file. // Read it from a secret store or the environment; never commit a real one. string userPassword = Environment.GetEnvironmentVariable("PDF_USER_PASSWORD") ?? "open-me";The user password is the one readers type to open the file. Read it from a secret store or an environment variable, as here;
open-meis only a placeholder for the sample. Do not write real passwords into source code or plain-text configuration files. -
5Create the PDF
// 2. Create the PDF. HtmlToPdf converter = new HtmlToPdf(); PdfDocument doc = converter.ConvertHtmlString(""" <h1>Payslip</h1> <p>Employee: Jane Doe</p> <p>Net pay: 3,250.00</p> """);HtmlToPdfconverts a small payslip to aPdfDocument. In your application this is any document you build or convert: an invoice, a statement, a report. -
6Encrypt it with AES-256
// 3. Encrypt it with AES-256 (these two lines are the defaults, written out). doc.Security.Algorithm = PdfEncryptionAlgorithm.AES; doc.Security.KeySize = PdfEncryptionKeySize.EncryptKey256BitRevision6; doc.Security.UserPassword = userPassword;
Securityholds the document's encryption settings.Algorithm = AESwithKeySize = EncryptKey256BitRevision6is AES-256, revision 6, and it is already the default, so the only line you need isUserPassword. They are written out here so the choice is visible in your code. -
7Save and close
// 4. Save and close. doc.Save("payslip-protected.pdf"); doc.Close();Savewrites the encrypted file andClosereleases the document. Openingpayslip-protected.pdfnow asks for the password. -
8Run it
dotnet run
The program writes
payslip-protected.pdfto the working folder. Without a license key, every page carries a trial watermark; setGlobalProperties.LicenseKeybefore the first conversion to remove it.
The complete Program.cs.
This is the exact file our samples pipeline compiled and ran on Linux ARM64 and Linux x64 and macOS ARM64 and Windows x64 to produce the output on this page.
using SelectPdf.Universal;
// 1. The password readers type to open the file.
// Read it from a secret store or the environment; never commit a real one.
string userPassword = Environment.GetEnvironmentVariable("PDF_USER_PASSWORD") ?? "open-me";
// 2. Create the PDF.
HtmlToPdf converter = new HtmlToPdf();
PdfDocument doc = converter.ConvertHtmlString("""
<h1>Payslip</h1>
<p>Employee: Jane Doe</p>
<p>Net pay: 3,250.00</p>
""");
// 3. Encrypt it with AES-256 (these two lines are the defaults, written out).
doc.Security.Algorithm = PdfEncryptionAlgorithm.AES;
doc.Security.KeySize = PdfEncryptionKeySize.EncryptKey256BitRevision6;
doc.Security.UserPassword = userPassword;
// 4. Save and close.
doc.Save("payslip-protected.pdf");
doc.Close();
Open the project on GitHub → this program and the variations below, ready to dotnet run
The PDF it produces.
Download payslip-protected.pdf ↓ the file this sample produced, unedited
Variations you will need next.
Each variation is a separate file in the same project, compiled and run with the program above.
Block printing and copying with an owner password
OwnerPassword is the password that changes the permissions. With it set, CanPrint, CanCopyContent and CanEditContent set to false turn those actions off. Compliant viewers honour the permissions, but they do not keep the content confidential: the user password does that, so set both.
using SelectPdf.Universal;
static class RestrictPermissions
{
public static void Run()
{
string userPassword = Environment.GetEnvironmentVariable("PDF_USER_PASSWORD") ?? "open-me";
string ownerPassword = Environment.GetEnvironmentVariable("PDF_OWNER_PASSWORD") ?? "owner-change-me";
HtmlToPdf converter = new HtmlToPdf();
PdfDocument doc = converter.ConvertHtmlString("<h1>Contract draft</h1><p>For review only.</p>");
// the user password opens the file; the owner password changes the permissions
doc.Security.UserPassword = userPassword;
doc.Security.OwnerPassword = ownerPassword;
// no printing, no copying, no editing
doc.Security.CanPrint = false;
doc.Security.CanCopyContent = false;
doc.Security.CanEditContent = false;
doc.Save("contract-restricted.pdf");
doc.Close();
}
}
Encrypt an existing PDF file
PdfSecurityManager loads a PDF you already have, sets the passwords and saves an encrypted copy, with the same AES-256 default. The new file is then opened again with new PdfDocument(fileName, password), the constructor for password-protected PDFs.
using SelectPdf.Universal;
static class EncryptExistingFile
{
public static void Run()
{
// a PDF that already exists (here made on the spot)
PdfDocument report = new HtmlToPdf().ConvertHtmlString("<h1>Annual report</h1>");
report.Save("report.pdf");
report.Close();
string userPassword = Environment.GetEnvironmentVariable("PDF_USER_PASSWORD") ?? "open-me";
string ownerPassword = Environment.GetEnvironmentVariable("PDF_OWNER_PASSWORD") ?? "owner-change-me";
// PdfSecurityManager encrypts an existing file (AES-256, revision 6, by default)
var security = new PdfSecurityManager();
security.Load("report.pdf");
security.UserPassword = userPassword;
security.OwnerPassword = ownerPassword;
security.Save("report-protected.pdf");
security.Close();
// open it again with the password
PdfDocument check = new PdfDocument("report-protected.pdf", userPassword);
Console.WriteLine($"Opened with the password: {check.Pages.Count} page(s)");
check.Close();
}
}
Encrypt during HTML to PDF conversion
converter.Options.SecurityOptions takes the same passwords and permissions, so every PDF the converter produces is encrypted as it is created, with AES-256 by default. Here copying is turned off for a converted web page.
using SelectPdf.Universal;
static class EncryptDuringConversion
{
public static void Run()
{
string userPassword = Environment.GetEnvironmentVariable("PDF_USER_PASSWORD") ?? "open-me";
string ownerPassword = Environment.GetEnvironmentVariable("PDF_OWNER_PASSWORD") ?? "owner-change-me";
// set the security on the converter: every PDF it produces is encrypted
HtmlToPdf converter = new HtmlToPdf();
converter.Options.SecurityOptions.UserPassword = userPassword;
converter.Options.SecurityOptions.OwnerPassword = ownerPassword;
converter.Options.SecurityOptions.CanCopyContent = false;
PdfDocument doc = converter.ConvertUrl("https://selectpdf.com");
doc.Save("page-protected.pdf");
doc.Close();
}
}
Common problems and fixes.
An old PDF reader cannot open the file
Readers older than Acrobat X cannot open AES-256. If you must support them, set Algorithm = PdfEncryptionAlgorithm.AES and KeySize = PdfEncryptionKeySize.EncryptKey128Bit. Avoid RC4: it is broken and kept only for old viewers.
Anyone can still open the PDF
Only an owner password was set. The owner password controls permissions; the user password is the one that has to be typed to open the document.
A PDF/A document with a password is refused
PDF/A does not allow encryption. Protect archived documents at the storage level instead.
A search index cannot read the document's metadata
By default the XMP metadata is encrypted with the content. Set EncryptMetadata = false to leave it readable. The title, author and keywords entries stay encrypted either way.
Good to know.
- Setting a password is enough: the document is encrypted with AES and a 256-bit key, revision 6 of the PDF security handler. This applies to a new document, to an existing document that is not encrypted, and to the HTML to PDF converter's security options. Source
- A document that is already encrypted keeps its own algorithm and key length unless they are changed. Source
- Readers older than Acrobat X cannot open AES-256; when such readers must be supported, choose AES with a 128-bit key rather than RC4. Source
- An owner password without a user password lets anyone open the document; the user password is what keeps the content confidential. Source
- AES-256-GCM is also available. It is defined by PDF 2.0, so choosing it writes the document as PDF 2.0, which older viewers may not open. Source
Questions, answered.
Short answers about this guide. The SelectPdf.Universal documentation covers every class and option.