Encrypt and Decrypt PDFs with Python pypdf
python pypdf encrypt decrypt and password protected pdf: Learn to encrypt and decrypt PDFs with Python's pypdf library, including user and owner passwords, permissions...
Working with password-protected PDFs in Python often means using the pypdf library for both encrypting and decrypting documents. Whether you need to add a password to a new PDF or open an existing protected file, pypdf provides the core methods to handle the job. This article walks through the essential operations for python pypdf encrypt decrypt and password protected PDF workflows.
Installing pypdf
pypdf is a pure-Python library, so installation is straightforward with pip:
pip install pypdf
The library is actively maintained and works on Python 3.6 and later. If you have an older project that used PyPDF2, pypdf is the successor and shares most of the API.
Encrypting a PDF with a user password
To encrypt a PDF, you create a PdfWriter object, add pages from a source PdfReader, and then call encrypt() with the desired passwords. The method returns a PdfWriter instance, but you typically call it before writing the output file.
from pypdf import PdfReader, PdfWriter reader = PdfReader("original.pdf") writer = PdfWriter() for page in reader.pages: writer.add_page(page) writer.encrypt(user_password="secret") with open("protected.pdf", "wb") as output_file: writer.write(output_file)
The encrypt() method accepts two password arguments: user_password and owner_password. If you only provide user_password, it is used for both roles. The user password is what a reader must enter to open the document. The owner password controls the permissions, such as printing or copying.
Setting owner password and permissions
The owner password is the master password. It allows the document to be opened with full permissions and is also used to change or remove the encryption. You can pass it explicitly:
writer.encrypt(user_password="user123", owner_password="owner456")
You can also restrict what a user can do with the document by passing the permissions parameter. pypdf defines constants for common permissions, such as pypdf.constants.AnnotationPermissions, but the most direct way is to use the PdfReader and PdfWriter permission flags. For example, to allow printing but not copying:
from pypdf import PdfWriter from pypdf.constants import Permission writer = PdfWriter() # ... add pages ... writer.encrypt( user_password="user123", owner_password="owner456", permissions=Permission.PRINT )
The Permission class provides flags like PRINT, COPY, MODIFY, ANNOTATE, and FILL_FORMS. You can combine them with bitwise OR:
permissions = Permission.PRINT | Permission.COPY
If you omit permissions, all permissions are granted to the user.
The following table summarizes the common permission flags and what they control:
| Flag | Allows the user to |
|---|---|
PRINT | Print the document |
COPY | Copy text and images from the document |
MODIFY | Modify the document content |
ANNOTATE | Add or modify annotations |
FILL_FORMS | Fill in form fields |
Decrypting a password-protected PDF
To read an encrypted PDF, you create a PdfReader and call decrypt() with the user or owner password. The method returns an integer that indicates the decryption status: 0 means the password was incorrect, 1 means the user password was accepted, and 2 means the owner password was accepted.
from pypdf import PdfReader reader = PdfReader("protected.pdf") result = reader.decrypt("user123") if result == 0: print("Incorrect password") else: # Access pages and text for page in reader.pages: print(page.extract_text())
Once decrypted, the reader object behaves like any other PDF. You can extract text, images, or copy pages to a new writer.
Handling errors and edge cases
A common mistake is trying to read an encrypted PDF without calling decrypt(). In that case, pypdf raises a FileNotDecryptedError when you attempt to access pages. The error message is clear, but it can be confusing if you expect an automatic prompt.
Another edge case is a PDF that uses an empty password. Some PDFs are encrypted with an empty user password. pypdf treats an empty string as a valid password, so decrypt("") may succeed. This is rare, but it is worth knowing.
If you need to remove encryption entirely, you can decrypt the PDF and then write it out without calling encrypt() on the new writer:
reader = PdfReader("protected.pdf") reader.decrypt("user123") writer = PdfWriter() for page in reader.pages: writer.add_page(page) with open("unprotected.pdf", "wb") as f: writer.write(f)
This creates a new PDF with no password protection.
Security considerations and limitations
PDF encryption in pypdf uses the standard RC4 or AES algorithms, depending on the PDF version and the encrypt() parameters. By default, pypdf uses AES-128 for newer PDFs, but you can specify algorithm in encrypt(). The important point is that PDF password protection is not a strong security boundary. It is designed to prevent casual access, not to withstand determined attackers. The user password is often the only thing protecting the content, and the encryption strength depends on the PDF reader's implementation.
Also, note that pypdf cannot decrypt PDFs that use a certificate-based encryption or certain proprietary encryption schemes. If you encounter such a file, you may need a different tool.
Performance and memory considerations
When encrypting or decrypting large PDFs, pypdf loads the entire file into memory. This is fine for typical documents, but for very large files you may want to process pages in a streaming fashion. However, pypdf does not support incremental encryption, so the whole document must be read and written. If memory is a concern, consider splitting the PDF into smaller chunks before encryption.
Final technical note: preserving metadata and structure
When you encrypt a PDF, the original metadata (title, author, etc.) is preserved, but the document structure is rewritten. This means that some interactive elements, such as JavaScript actions, may be lost. If you need to retain those, you should test the output with your target PDF reader. Also, be aware that the encrypt() method modifies the writer's state; you cannot call it twice on the same writer without resetting it.