PDF Passwords and Permissions
A PDF carries its own protection: a user password that encrypts it and a master password that guards a set of permissions for PDF readers. The two are easy to confuse, and only the first keeps a document confidential. For a WPF application that saves protected PDF documents or opens those of others, what follows explains what each setting achieves, how the permissions of an opened PDF reach the TextControl and how to prove that a saved file is encrypted.
The save settings themselves, with code for each permission, are shown in Passwords and Access Permissions.
Passwords and Permissions Compared
Three save settings protect a PDF: UserPassword encrypts the document so that it can't be opened without the password, MasterPassword guards the permissions, and DocumentAccessPermissions lists what readers may do, which takes effect only together with a master password. How PDF Passwords Work describes the properties and the values of the permissions.
Why Permissions Aren't Enough
Text Control's articles on PDF security make four points:
- A permission is a request to the reader. Compliant readers such as Adobe Acrobat honor it, and others can ignore it. Permissions are no cryptographic protection.
- Confidential content needs encryption. Use a user password, and treat permissions as a policy added to it.
- The master password grants everything. Opening the document with it gives all permissions. Text Control therefore recommends a master password that differs from the user password.
- Neither password proves integrity. To show that a document wasn't changed and who created it, sign it, as Certificate and Private Key Handling explains.
Respect the Permissions of a PDF
TX Text Control decrypts an encrypted PDF when it loads it, and the rest is up to your application: getting the passwords from the user and restricting operations that the document doesn't allow. The permissions are available after loading in LoadSettings.DocumentAccessPermissions. With the correct master password, they are AllowAll.
The following method in MainWindow.xaml.cs loads a PDF into textControl1 and turns its permissions into restrictions of the editor. The file needs using TXTextControl;:
private bool LoadPdf(string path, string userPassword)
{
LoadSettings loadSettings = new LoadSettings()
{
UserPassword = userPassword
};
try
{
textControl1.Load(path, StreamType.AdobePDF, loadSettings);
}
catch (FilterException exception) when (exception.Reason == FilterException.FilterError.InvalidPassword)
{
// Missing or wrong user password: ask the user and call LoadPdf again
return false;
}
// Apply the permissions of the PDF to the editor
DocumentAccessPermissions permissions = loadSettings.DocumentAccessPermissions;
if (permissions != DocumentAccessPermissions.AllowAll)
{
DocumentPermissions editorPermissions = textControl1.DocumentPermissions;
editorPermissions.ReadOnly = !permissions.HasFlag(DocumentAccessPermissions.AllowGeneralEditing);
editorPermissions.AllowFormatting = permissions.HasFlag(DocumentAccessPermissions.AllowGeneralEditing);
editorPermissions.AllowCopy = permissions.HasFlag(DocumentAccessPermissions.AllowExtractContents);
editorPermissions.AllowPrinting = permissions.HasFlag(DocumentAccessPermissions.AllowLowLevelPrinting)
|| permissions.HasFlag(DocumentAccessPermissions.AllowHighLevelPrinting);
editorPermissions.AllowEditingFormFields = permissions.HasFlag(DocumentAccessPermissions.AllowAuthoringFields);
textControl1.EditMode = TXTextControl.EditMode.ReadAndSelect;
}
return true;
}
The code works as follows:
- WPF.TextControl.Load throws a FilterException with the Reason
InvalidPasswordif the user password is missing or wrong. Then the method returnsfalse, and your window asks for the password and calls it again. - The permissions are flags that can be combined, such as
AllowGeneralEditing,AllowExtractContents,AllowHighLevelPrintingandAllowAuthoringFields.AllowAllincludes every flag, so a PDF without restrictions stays fully editable. - The flags set the properties of the DocumentPermissions object that WPF.TextControl.DocumentPermissions returns, and EditMode set to
ReadAndSelectactivates them. Before the next document is loaded, setEditModeback toEditMode.Edit. - Commands of your own, such as a print button, check CanPrint or CanCopy before they run.
Some PDF documents can't be decrypted at all: AES with a 256-bit key and certificate-based encryption make Load throw a FilterException with the reason Encrypted. Encrypted PDF Documents lists what TX Text Control imports.
Check That a PDF Is Encrypted
A process that forgets the user password produces a readable PDF without any error. Text Control's advice for automated workflows is to check the result: loading a PDF that has a user password fails without it. This method returns true when that is the case:
private static bool IsEncrypted(string path)
{
using (ServerTextControl tx = new ServerTextControl())
{
tx.Create();
try
{
// Loading without a password must fail
tx.Load(path, StreamType.AdobePDF);
return false;
}
catch (FilterException exception) when (exception.Reason == FilterException.FilterError.InvalidPassword)
{
return true;
}
}
}
The code works as follows:
- A ServerTextControl has no user interface, so the check doesn't touch the document in the window. Create comes before any other member.
- When Load succeeds, the file opens without a password and isn't encrypted. Load errors with other reasons, such as a damaged file, are passed on to the caller.
In the blog article PDF Security Explained: Passwords, Permissions, Encryption and Digital Signatures in C# .NET, the check also loads the file with the password to confirm that it opens.
Next Steps
- Passwords and Access Permissions: the code for saving protected PDF documents.
- Handling Untrusted Documents: accepted formats, load errors and links.
- Certificate and Private Key Handling: sign what you encrypt.
- The blog article PDF Security for C# Developers: Encryption and Permissions in .NET: the permission values and their effect in PDF readers.