As someone getting into cybersecurity, I wanted my first real project to be something more than a tutorial follow-along — I wanted to actually understand every line I wrote, not just make something that runs. So I built a command-line File Encryption Tool in Python, and along the way I hit (and fixed) a handful of bugs that taught me more than getting it right the first time ever would have.
Code: github.com/Nikunj-cyber/File-Encryption-Tool
What it does
- Generates a secure encryption key on first run, and reuses it on every run after
- Encrypts any file — text, images, PDFs, anything — into unreadable ciphertext
- Decrypts it back to the exact original bytes
- Keeps the encryption key out of Git entirely, so it never ends up on GitHub
The stack
- Python 3
-
cryptographylibrary — specificallyFernet, which combines AES-128 encryption with HMAC message authentication - Git/GitHub for version control
How it's structured
Rather than one big script, I split it into small, single-responsibility modules:
main.py → orchestrates the program
key_manager.py → generates, saves, and loads the encryption key
encrypt.py → reads a file and encrypts it
decrypt.py → reverses the process
main.py checks whether a key already exists. If not, it generates one and saves it; if it does, it just loads it. Either way, it then calls encrypt_file() or decrypt_file(), which stay completely unaware of how the key came to exist — they just trust it's there. That separation turned out to be a genuinely useful lesson in its own right (more below).
The bugs that actually taught me something
1. A circular import, right at the start.
I initially put from key_manager import generate_key, save_key, load_key inside key_manager.py itself instead of main.py — meaning the file was trying to import itself, forever. First real lesson in where imports actually belong and why.
2. An inverted if/else that would've silently destroyed my key.
At one point my logic read:
if not os.path.exists("keys/secret.key"):
key = load_key()
else:
key = generate_key()
save_key(key)
Read that literally: if the key is missing, load it (impossible — nothing to load), if it exists, generate and overwrite it (meaning every single run after the first would silently destroy the previous key and make any already-encrypted file permanently unrecoverable). This one scared me a little once I understood what it actually meant — no error, no crash, just quietly wrong, forever. Fixing it taught me to read conditionals word-by-word instead of skimming them.
3. Forgetting that return exits a function immediately.
I tried adding a print() confirmation after a return statement, not realizing that code is permanently unreachable — Python leaves the function the instant it hits return. Small thing, but it's the kind of bug that's invisible until you actually understand why it's invisible.
4. The "did I actually save the file?" trap — twice.
I ran into ModuleNotFoundError and FileNotFoundError messages that referenced code I'd already fixed in my editor. Turned out I'd been running python main.py without saving first, so Python was executing a stale version from disk. Now I check for the unsaved-file dot in VS Code before running anything, every time.
A design decision I'd actually defend in an interview
Early on I had to decide: should encryption overwrite the original file, or write a new one? I went with always writing a new file (file.txt → file.txt.encrypted), never touching the original. The reasoning: if the program crashes mid-write, an in-place overwrite could leave you with a half-written, corrupted file — and the original would already be gone. Writing to a new file means the original stays untouched no matter what goes wrong. Small decision, but it's the kind of "what happens when this fails halfway" thinking that actually matters in security-adjacent tooling.
What's next
- Handling
InvalidTokengracefully when someone tries decrypting with the wrong key or a corrupted file - Accepting the target file as a command-line argument instead of a hardcoded path
- Possibly adding password-derived keys (PBKDF2) as an alternative to the random key, for portability
If you're also early in a cybersecurity/dev journey — I'd rather ship something small and actually understand every line than copy-paste something bigger. Happy to answer questions about any part of this if it's useful to you.
Top comments (0)