-
-
Notifications
You must be signed in to change notification settings - Fork 372
PDF Encryption and Decryption #496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
# PDF Encrypter/Decrypter Application | ||
# Made By Soriful Islam Sk | ||
# Requires: pip install pypdf cryptography | ||
# ------------------------------------------------------------------- | ||
|
||
import tkinter as tk | ||
from tkinter import filedialog, messagebox | ||
from pypdf import PdfReader, PdfWriter | ||
import os | ||
|
||
# --- CORE ENCRYPTION/DECRYPTION LOGIC --- | ||
|
||
def encrypt_pdf_file(input_path, output_path, user_password, owner_password): | ||
"""Encrypts a PDF file using AES-256 and sets permissions.""" | ||
try: | ||
reader = PdfReader(input_path) | ||
# Clone pages from reader to a new writer object | ||
writer = PdfWriter(clone_from=reader) | ||
|
||
# Set AES-256 encryption and restrict user permissions | ||
writer.encrypt( | ||
user_password=user_password, | ||
owner_password=owner_password, | ||
permissions_flag=0xFFC0, # Common flag to restrict editing/copying for the user | ||
algorithm="AES-256" | ||
) | ||
|
||
with open(output_path, "wb") as output_file: | ||
writer.write(output_file) | ||
|
||
return True, f"Encrypted and saved as:\n{output_path}" | ||
except Exception as e: | ||
return False, f"Encryption Error: {e}" | ||
|
||
def decrypt_pdf_file(input_path, output_path, user_password, owner_password): | ||
""" | ||
Decrypts a password-protected PDF file by attempting the user_password | ||
first, and then falling back to the owner_password. | ||
""" | ||
try: | ||
reader = PdfReader(input_path) | ||
|
||
if not reader.is_encrypted: | ||
# If the file isn't encrypted, just save a copy and inform the user. | ||
writer = PdfWriter(clone_from=reader) | ||
with open(output_path, "wb") as output_file: | ||
writer.write(output_file) | ||
return True, f"File was not encrypted. Saved copy as:\n{output_path}" | ||
|
||
# 1. ATTEMPT DECRYPTION WITH USER PASSWORD | ||
result = reader.decrypt(user_password) | ||
|
||
if result not in (1, 0): # 1 and 0 indicate successful decryption | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Magic numbers 1 and 0 for decryption success should be replaced with named constants or use the pypdf library's defined constants if available. This would make the code more readable and maintainable. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||
# 2. FALLBACK: ATTEMPT DECRYPTION WITH OWNER PASSWORD | ||
result = reader.decrypt(owner_password) | ||
|
||
# 3. CHECK FINAL DECRYPTION STATUS | ||
if result not in (1, 0): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same magic numbers 1 and 0 repeated. Consider defining constants like DECRYPTION_SUCCESS_CODES = (0, 1) at the module level and using them consistently throughout the decryption logic. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||
return False, "Decryption Error: Neither the User nor the Owner password worked." | ||
|
||
# Decryption was successful (either password worked) | ||
writer = PdfWriter(clone_from=reader) | ||
|
||
with open(output_path, "wb") as output_file: | ||
writer.write(output_file) | ||
|
||
return True, f"Decrypted and saved as:\n{output_path}" | ||
|
||
except Exception as e: | ||
return False, f"Decryption Error: {e}" | ||
|
||
# --- GUI APPLICATION CLASS --- | ||
|
||
class PDFSecurityApp: | ||
def __init__(self, master): | ||
self.master = master | ||
master.title("PDF Encrypter/Decrypter") | ||
# Increased height slightly to accommodate the new label | ||
master.geometry("450x330") | ||
|
||
self.input_file_path = tk.StringVar() | ||
self.user_pass = tk.StringVar() | ||
self.owner_pass = tk.StringVar() | ||
|
||
# 1. File Selection Section | ||
tk.Label(master, text="Select PDF File:").grid(row=0, column=0, padx=10, pady=10, sticky="w") | ||
self.file_entry = tk.Entry(master, textvariable=self.input_file_path, width=40) | ||
self.file_entry.grid(row=0, column=1, padx=5, pady=10) | ||
tk.Button(master, text="Browse", command=self.browse_file).grid(row=0, column=2, padx=5, pady=10) | ||
|
||
# 2. Password Entry Section (User/Open Password) | ||
tk.Label(master, text="User/Open Password:").grid(row=1, column=0, padx=10, pady=5, sticky="w") | ||
tk.Entry(master, textvariable=self.user_pass, show="*", width=30).grid(row=1, column=1, padx=5, pady=5) | ||
|
||
# 3. Owner/Admin Password | ||
tk.Label(master, text="Owner/Admin Password:").grid(row=2, column=0, padx=10, pady=5, sticky="w") | ||
tk.Entry(master, textvariable=self.owner_pass, show="*", width=30).grid(row=2, column=1, padx=5, pady=5) | ||
|
||
# 4. Action Buttons Section | ||
button_frame = tk.Frame(master) | ||
button_frame.grid(row=3, column=0, columnspan=3, pady=20) | ||
|
||
tk.Button(button_frame, text="🔒 ENCRYPT PDF", command=self.handle_encrypt, | ||
bg="lightblue", padx=10, pady=5).pack(side=tk.LEFT, padx=10) | ||
|
||
tk.Button(button_frame, text="🔓 DECRYPT PDF", command=self.handle_decrypt, | ||
bg="lightgreen", padx=10, pady=5).pack(side=tk.LEFT, padx=10) | ||
|
||
# 5. Author Credit Label | ||
tk.Label( | ||
master, | ||
text="Made By Soriful Islam Sk", | ||
fg="gray", | ||
font=("Arial", 9, "italic") | ||
).grid(row=4, column=0, columnspan=3, pady=(10, 5)) | ||
|
||
def browse_file(self): | ||
"""Opens a dialog to select the input PDF file.""" | ||
file_path = filedialog.askopenfilename( | ||
defaultextension=".pdf", | ||
filetypes=[("PDF files", "*.pdf"), ("All files", "*.*")] | ||
) | ||
if file_path: | ||
self.input_file_path.set(file_path) | ||
|
||
def get_output_path(self, operation): | ||
"""Generates the output file path based on the operation.""" | ||
input_path = self.input_file_path.get() | ||
if not input_path: | ||
return None | ||
|
||
base, ext = os.path.splitext(input_path) | ||
default_output = f"{base}_{operation}{ext}" | ||
|
||
return filedialog.asksaveasfilename( | ||
defaultextension=ext, | ||
initialfile=default_output, | ||
filetypes=[("PDF files", "*.pdf")] | ||
) | ||
|
||
def handle_encrypt(self): | ||
"""Handles the Encrypt button click.""" | ||
input_path = self.input_file_path.get() | ||
user_p = self.user_pass.get() | ||
owner_p = self.owner_pass.get() | ||
|
||
if not input_path or not user_p: | ||
messagebox.showerror("Input Error", "Please select a file and enter a User Password.") | ||
return | ||
|
||
# Use user password as owner password if owner field is left empty | ||
if not owner_p: | ||
owner_p = user_p | ||
|
||
output_path = self.get_output_path("encrypted") | ||
if not output_path: | ||
return | ||
|
||
success, message = encrypt_pdf_file(input_path, output_path, user_p, owner_p) | ||
|
||
if success: | ||
messagebox.showinfo("Success", f"Encryption Complete! {message}") | ||
else: | ||
messagebox.showerror("Error", message) | ||
|
||
def handle_decrypt(self): | ||
"""Handles the Decrypt button click by trying both passwords.""" | ||
input_path = self.input_file_path.get() | ||
user_p = self.user_pass.get() | ||
owner_p = self.owner_pass.get() | ||
|
||
# Check that at least one password is provided for decryption attempt | ||
if not input_path or (not user_p and not owner_p): | ||
messagebox.showerror( | ||
"Input Error", | ||
"Please select a file and enter at least one password (User or Owner) for decryption." | ||
) | ||
return | ||
|
||
output_path = self.get_output_path("decrypted") | ||
if not output_path: | ||
return | ||
|
||
# Pass both entered passwords for sequential trial in the decryption function | ||
success, message = decrypt_pdf_file(input_path, output_path, user_p, owner_p) | ||
|
||
if success: | ||
messagebox.showinfo("Success", f"Decryption Complete! {message}") | ||
else: | ||
messagebox.showerror("Error", message) | ||
|
||
|
||
# --- RUN THE APPLICATION --- | ||
if __name__ == "__main__": | ||
root = tk.Tk() | ||
app = PDFSecurityApp(root) | ||
root.mainloop() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Magic number 0xFFC0 should be documented or replaced with named constants. Consider defining this as a constant with a descriptive name like RESTRICTED_USER_PERMISSIONS and documenting what specific permissions it grants/restricts.
Copilot uses AI. Check for mistakes.