Want to protect your sensitive files from unauthorized access? In this project, you'll build a File Encryption & Decryption Tool using Python, PyQt, and the cryptography
library to ensure your files remain secure.
What You Will Learn:
- Using the
cryptography
library to encrypt and decrypt files - Implementing AES encryption for strong security
- Creating a PyQt-based GUI for user-friendly file selection
- Adding password-based encryption for extra protection
- Styling the UI for a modern and professional look
Step 1: Setting Up the Project
Before we start coding, let’s set up our Python project:
1. Make sure Python is installed on your computer. If not, download it from the official Python website.
2. Open your favorite code editor or IDE.
3. Create a new Python file, for example, file_encryptor.py
.
Note, if you don't have PyQt or cryptography installed, you'll need to run the following install command:
pip install pyqt5 cryptography
Great, now, let's dive head first into our Python editor to get this build started to produce something like I've shown below:
Step 2: Understanding the Project
Our File Encryption & Decryption Tool will:
- Allow users to select files via a PyQt-based file picker.
- Encrypt files using AES encryption for strong security.
- Require a password to decrypt files.
- Provide a simple and intuitive UI.
And don't worry if you're not sure how to create a GUI with PyQt, I've got you covered. Plus, I've also created a detailed guide on how to use PyQt if you want some extra help.
Step 3: Importing Required Modules
We need the following Python modules:
import sys
import os
import base64
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QFileDialog, QMessageBox, QLineEdit
from PyQt5.QtGui import QFont
from cryptography.fernet import Fernet
Why These Modules?
sys
→ Required for running the PyQt application.os
→ Helps with file handling.base64
→ Encodes and decodes keys in a secure format.PyQt5.QtWidgets
→ Provides UI elements like buttons, labels, and file dialogs.PyQt5.QtGui
→ Allows for font customization and enhanced UI styling.cryptography.fernet
→ Implements AES encryption for secure file handling.
Step 4: Sketching Out the Class and Methods
We’ll structure our PyQt5 GUI application using a class-based approach via OOP in Python.
class FileEncryptor(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
"""Initializes the UI layout and widgets."""
pass
def select_file(self):
"""Opens a file dialog for selecting the file to encrypt or decrypt."""
pass
def encrypt_file(self, file_path, password):
"""Encrypts the selected file using AES encryption."""
pass
def decrypt_file(self, file_path, password):
"""Decrypts the selected file using AES decryption."""
pass
How It Works:
-
Encapsulation of Functionality:
- The
FileEncryptor
class encapsulates the UI setup and encryption logic. - This makes it easy to expand features like key management or batch encryption.
- The
-
Class Constructor (
__init__
Method):- Calls
super().__init__()
to inherit fromQWidget
, enabling GUI functionality. - Calls
self.init_ui()
to initialize the graphical interface.
- Calls
-
init_ui()
Method:- This method sets up the GUI layout, including file selection, password entry, and encryption buttons.
- Ensures a smooth and user-friendly experience.
-
select_file()
Method:- Opens a file selection dialog to let users choose a file for encryption or decryption.
- Stores the selected file path for further processing.
-
encrypt_file()
&decrypt_file()
Methods:- Handles the actual encryption and decryption using AES.
- Uses password-based encryption for added security.
By structuring the program this way, we ensure a well-organized, scalable, and maintainable application while keeping the logic modular and easy to modify.
Step 5: Implementing the UI with PyQt5
def init_ui(self):
"""Initializes the UI layout and widgets with styling."""
self.setWindowTitle("File Encryption & Decryption Tool")
self.setGeometry(100, 100, 450, 300)
# Apply custom font and layout
app_font = QFont("Arial", 12)
self.setFont(app_font)
layout = QVBoxLayout()
self.label = QLabel("Select a file to encrypt or decrypt.")
self.label.setStyleSheet("color: #FF0000; font-size: 14px; font-weight: bold;")
layout.addWidget(self.label)
self.password_input = QLineEdit(self)
self.password_input.setPlaceholderText("Enter password")
self.password_input.setEchoMode(QLineEdit.Password)
self.password_input.setStyleSheet("""
QLineEdit {
border: 2px solid #FF0000;
border-radius: 5px;
padding: 5px;
font-size: 14px;
}
"""
)
layout.addWidget(self.password_input)
self.select_button = QPushButton("Select File")
self.select_button.setStyleSheet("""
QPushButton {
background-color: #FF0000;
color: white;
border-radius: 5px;
padding: 10px;
font-size: 14px;
font-weight: bold;
}
QPushButton:hover {
background-color: #CC0000;
}
"""
)
self.select_button.clicked.connect(self.select_file)
layout.addWidget(self.select_button)
self.encrypt_button = QPushButton("Encrypt File")
self.encrypt_button.setStyleSheet("""
QPushButton {
background-color: #008000;
color: white;
border-radius: 5px;
padding: 10px;
font-size: 14px;
font-weight: bold;
}
QPushButton:hover {
background-color: #006600;
}
"""
)
self.encrypt_button.clicked.connect(lambda: self.encrypt_file(self.file_path, self.password_input.text()) if hasattr(self, 'file_path') else None)
layout.addWidget(self.encrypt_button)
self.decrypt_button = QPushButton("Decrypt File")
self.decrypt_button.setStyleSheet("""
QPushButton {
background-color: #000080;
color: white;
border-radius: 5px;
padding: 10px;
font-size: 14px;
font-weight: bold;
}
QPushButton:hover {
background-color: #000066;
}
"""
)
self.decrypt_button.clicked.connect(lambda: self.decrypt_file(self.file_path, self.password_input.text()) if hasattr(self, 'file_path') else None)
layout.addWidget(self.decrypt_button)
self.setLayout(layout)
# Apply overall window styling
self.setStyleSheet("""
QWidget {
background-color: #000000;
color: white;
}
"""
)
How It Works:
-
Encapsulation of UI Setup:
-
The
init_ui()
method initializes all UI components in one place for clarity and maintainability. -
Ensures a clean separation of logic between UI and functionality.
-
-
UI Components:
-
A label provides instructions to the user.
-
A
QLineEdit
field allows users to enter a password for encryption and decryption. -
A
Select File
button enables users to choose a file. -
Encrypt File
andDecrypt File
buttons trigger the respective processes.
-
-
Theming and Styling:
-
A red, green, and blue color scheme enhances visual clarity.
-
Buttons have hover effects to improve user interaction.
-
The window background is black for a sleek, modern look.
-
By structuring and styling the UI this way, we ensure that the File Encryption & Decryption Tool is both visually appealing and user-friendly, making it simple for users to protect their sensitive data.
This approach ensures clean, efficient, and dynamic event handling, making the File Encryption & Decryption Tool more user-friendly and intuitive.
Understanding Lambda Functions in Button Click Events
In our UI, we use Python lambda functions to connect button clicks to encryption and decryption actions:
self.encrypt_button.clicked.connect(lambda: self.encrypt_file(self.file_path, self.password_input.text()) if hasattr(self, 'file_path') else None)
self.decrypt_button.clicked.connect(lambda: self.decrypt_file(self.file_path, self.password_input.text()) if hasattr(self, 'file_path') else None)
How It Works:
-
Lambda Functions as Event Handlers:
-
A lambda function is a short, anonymous function that executes when the button is clicked.
-
Instead of defining a separate method for handling clicks, we use a lambda to dynamically call
encrypt_file
ordecrypt_file
with the appropriate parameters.
-
-
Checking for a Selected File (
hasattr(self, 'file_path')
):-
The lambda first checks if
self.file_path
exists to ensure a file has been selected. -
If
self.file_path
exists, it callsencrypt_file()
ordecrypt_file()
with the selected file and the password entered by the user. -
If no file is selected, it does nothing, preventing errors.
-
-
Passing User Input Dynamically:
-
The
password_input.text()
method fetches the current password entered by the user at the moment of the button click. -
This ensures that the correct password is used for encryption or decryption.
-
Why Use Lambda Functions Here?
-
Simplifies the Code: Instead of defining multiple methods just for handling button clicks, we directly map the button event to its corresponding function call.
-
Ensures Parameters Are Passed Dynamically: Unlike
connect(self.encrypt_file)
, which would require predefined arguments, the lambda allows passingself.file_path
andself.password_input.text()
at runtime. -
Prevents Unnecessary Function Calls: The function only executes when the button is clicked, ensuring efficient and controlled behavior.
Step 6: Implementing File Selection
def select_file(self):
"""Opens a file dialog for selecting the file to encrypt or decrypt."""
file_dialog = QFileDialog()
file_path, _ = file_dialog.getOpenFileName(self, "Select File to Encrypt/Decrypt")
if file_path:
self.file_path = file_path
self.label.setText(f"Selected: {os.path.basename(file_path)}")
How It Works:
-
Encapsulation of File Selection:
-
The
select_file()
method centralizes the logic for selecting a file. -
This modular approach allows for easy future enhancements, such as multi-file selection or filtering specific file types.
-
-
Opening the File Dialog:
-
Uses
QFileDialog.getOpenFileName()
to open a system-native file selection window. -
This ensures compatibility with different operating systems and a smooth user experience.
-
-
Storing the Selected File Path:
-
Once a file is selected, its path is stored in
self.file_path
. -
This variable is referenced later during encryption or decryption operations.
-
-
Updating the UI Dynamically:
-
After selecting a file, the
QLabel
updates to display the file name with a Python f-string. -
This provides immediate feedback to the user, confirming the file selection.
-
By structuring the file selection this way, we ensure an intuitive, user-friendly experience while maintaining a scalable and maintainable design.
Step 7: Implementing File Encryption & Decryption
def decrypt_file(self, file_path, password):
"""Decrypts the selected file using AES decryption."""
key = base64.urlsafe_b64encode(password.ljust(32).encode('utf-8'))
cipher = Fernet(key)
try:
with open(file_path, "rb") as file:
encrypted_data = file.read()
decrypted_data = cipher.decrypt(encrypted_data)
new_file_path = file_path.replace(".enc", "")
with open(new_file_path, "wb") as file:
file.write(decrypted_data)
self.label.setText("File successfully decrypted!")
except Exception as e:
self.label.setText("Error: Decryption failed.")
How It Works:
-
Encapsulation of Encryption & Decryption:
-
The
encrypt_file()
anddecrypt_file()
methods centralize security operations. -
This makes it easy to modify encryption strength, key handling, or add new security features.
-
-
Password-Based Key Generation:
-
The user-provided password is padded to 32 bytes and encoded using
base64.urlsafe_b64encode()
. -
This ensures that the key length matches AES encryption requirements.
-
-
File Encryption Process:
-
Reads the file contents into memory.
-
Encrypts the contents using AES via
Fernet(cipher).encrypt()
. -
Saves the encrypted data to a new file with a
.enc
extension.
-
-
File Decryption Process:
-
Reads the encrypted file’s contents.
-
Decrypts the data using the same password-based AES key.
-
Saves the decrypted contents as a new file, removing the
.enc
extension.
-
-
Handling Errors & Updating the UI:
-
If encryption or decryption fails (e.g., wrong password, corrupt file), the UI displays an error message via our try-except block.
-
Success messages are displayed after operations complete to provide feedback to the user.
-
By implementing encryption and decryption this way, we ensure strong security, ease of use, and a user-friendly approach to protecting sensitive data.
Step 8: Running the PyQt5 App
if __name__ == "__main__":
app = QApplication(sys.argv)
window = FileEncryptor()
window.show()
sys.exit(app.exec_())
How It Works:
-
Initializing the Application:
-
The
QApplication
object is responsible for managing the GUI event loop and user interactions. -
sys.argv
allows proper handling of command-line arguments, though they are not needed in this project.
-
-
Creating an Instance of the File Encryptor:
-
window = FileEncryptor()
initializes the UI and loads all graphical elements. -
This ensures that all widgets, buttons, and input fields are properly set up before being displayed.
-
-
Displaying the Application Window:
-
window.show()
makes the PyQt5 application visible to the user. -
Without this step, the GUI would not appear.
-
-
Starting the Event Loop:
-
sys.exit(app.exec_())
starts the PyQt5 event loop, ensuring that the application continues running until the user manually closes it. -
This event loop listens for interactions, such as button clicks and window events.
-
By structuring the application this way, we ensure that the File Encryption & Decryption Tool runs smoothly and efficiently, allowing users to interact with an intuitive and responsive GUI while securely managing their files.
Final Code: File Encryption Tool
import sys
import os
import base64
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QFileDialog, QMessageBox, QLineEdit
from PyQt5.QtGui import QFont
from cryptography.fernet import Fernet
class FileEncryptor(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
"""Initializes the UI layout and widgets with styling."""
self.setWindowTitle("File Encryption & Decryption Tool")
self.setGeometry(100, 100, 450, 300)
# Apply custom font and layout
app_font = QFont("Arial", 12)
self.setFont(app_font)
layout = QVBoxLayout()
self.label = QLabel("Select a file to encrypt or decrypt.")
self.label.setStyleSheet("color: #FF0000; font-size: 14px; font-weight: bold;")
layout.addWidget(self.label)
self.password_input = QLineEdit(self)
self.password_input.setPlaceholderText("Enter password")
self.password_input.setEchoMode(QLineEdit.Password)
self.password_input.setStyleSheet("""
QLineEdit {
border: 2px solid #FF0000;
border-radius: 5px;
padding: 5px;
font-size: 14px;
}
"""
)
layout.addWidget(self.password_input)
self.select_button = QPushButton("Select File")
self.select_button.setStyleSheet("""
QPushButton {
background-color: #FF0000;
color: white;
border-radius: 5px;
padding: 10px;
font-size: 14px;
font-weight: bold;
}
QPushButton:hover {
background-color: #CC0000;
}
"""
)
self.select_button.clicked.connect(self.select_file)
layout.addWidget(self.select_button)
self.encrypt_button = QPushButton("Encrypt File")
self.encrypt_button.setStyleSheet("""
QPushButton {
background-color: #008000;
color: white;
border-radius: 5px;
padding: 10px;
font-size: 14px;
font-weight: bold;
}
QPushButton:hover {
background-color: #006600;
}
"""
)
self.encrypt_button.clicked.connect(lambda: self.encrypt_file(self.file_path, self.password_input.text()) if hasattr(self, 'file_path') else None)
layout.addWidget(self.encrypt_button)
self.decrypt_button = QPushButton("Decrypt File")
self.decrypt_button.setStyleSheet("""
QPushButton {
background-color: #000080;
color: white;
border-radius: 5px;
padding: 10px;
font-size: 14px;
font-weight: bold;
}
QPushButton:hover {
background-color: #000066;
}
"""
)
self.decrypt_button.clicked.connect(lambda: self.decrypt_file(self.file_path, self.password_input.text()) if hasattr(self, 'file_path') else None)
layout.addWidget(self.decrypt_button)
self.setLayout(layout)
# Apply overall window styling
self.setStyleSheet("""
QWidget {
background-color: #000000;
color: white;
}
"""
)
def select_file(self):
"""Opens a file dialog for selecting the file to encrypt or decrypt."""
file_dialog = QFileDialog()
file_path, _ = file_dialog.getOpenFileName(self, "Select File to Encrypt/Decrypt")
if file_path:
self.file_path = file_path
self.label.setText(f"Selected: {os.path.basename(file_path)}")
def encrypt_file(self, file_path, password):
"""Encrypts the selected file using AES encryption."""
key = base64.urlsafe_b64encode(password.ljust(32).encode('utf-8'))
cipher = Fernet(key)
try:
with open(file_path, "rb") as file:
file_data = file.read()
encrypted_data = cipher.encrypt(file_data)
with open(file_path + ".enc", "wb") as file:
file.write(encrypted_data)
self.label.setText("File successfully encrypted!")
except Exception as e:
self.label.setText("Error: Encryption failed.")
def decrypt_file(self, file_path, password):
"""Decrypts the selected file using AES decryption."""
key = base64.urlsafe_b64encode(password.ljust(32).encode('utf-8'))
cipher = Fernet(key)
try:
with open(file_path, "rb") as file:
encrypted_data = file.read()
decrypted_data = cipher.decrypt(encrypted_data)
new_file_path = file_path.replace(".enc", "")
with open(new_file_path, "wb") as file:
file.write(decrypted_data)
self.label.setText("File successfully decrypted!")
except Exception as e:
self.label.setText("Error: Decryption failed.")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = FileEncryptor()
window.show()
sys.exit(app.exec_())
Wrapping Up
Nice work! You’ve built a Python File Encryption & Decryption Tool with PyQt5 that ensures files can be securely encrypted and decrypted with password protection.
What You Learned:
-
Using PyQt5 to create a GUI-based tool
-
Implementing AES encryption for secure file handling
-
Structuring Python programs with OOP principles
-
Handling password-based encryption securely
-
Managing file selection with
QFileDialog
Next Steps:
-
Allow users to generate and store encryption keys separately.
-
Implement support for encrypting multiple files in batch mode.
-
Add an option to choose between different encryption algorithms.
-
Introduce a secure password hashing mechanism to strengthen security.
The best way to learn is by building projects—so keep experimenting!