C++ File Encryptor/Decryptor Project
Scramble any file with a key so it can't be read — then unscramble it.
What you will build
Read a file byte by byte, XOR each byte with a key, and write the scrambled result; run it again with the same key to restore the original. It's a real (if basic) cipher, and it teaches the binary file handling that underpins compression, media, and security tools.
What you will learn
- Opening files in binary mode
- Reading/writing raw bytes with streams
- How XOR encryption works (and why it's reversible)
- Handling files of any type, not just text
Feature checklist
- Encrypt any file with a numeric key
- Decrypt with the same key to restore the original exactly
- Works on text, images, any binary file
- Reports bytes processed
- Refuses to overwrite the input file by accident
Starter code
#include <iostream>
#include <fstream>
#include <string>
bool transform(const std::string& inPath,
const std::string& outPath, unsigned char key) {
std::ifstream in(inPath, std::ios::binary);
std::ofstream out(outPath, std::ios::binary);
if (!in || !out) return false;
char byte;
while (in.get(byte)) {
byte = static_cast<char>(byte ^ key); // XOR flips bits
out.put(byte); // same op decrypts
}
return true;
}
int main() {
std::string in, out; int key;
std::cout << "Input file: "; std::cin >> in;
std::cout << "Output file: "; std::cin >> out;
std::cout << "Key (0-255): "; std::cin >> key;
if (transform(in, out, static_cast<unsigned char>(key)))
std::cout << "Done. Keep the key safe!\n";
else
std::cout << "Could not open files.\n";
}Full walkthrough
Step 1: Understand the one idea: XOR with a key
Encryption here is a single reversible operation — XOR every byte with the key byte. XOR has a magic property: (b ^ k) ^ k == b, so the SAME transform() function encrypts and decrypts. There is no separate decrypt algorithm to write or to get wrong; applying the transform twice restores the original exactly.
Step 2: Write transform() over a whole stream
transform(std::istream& in, std::ostream& out, char key) reads one char at a time with in.get(c), writes c ^ key, and counts the bytes so main() can report them. Working through stream references instead of filenames keeps the function reusable for any source — files today, anything else later.
void transform(std::istream& in, std::ostream& out, char key) {
char c;
while (in.get(c)) out.put(c ^ key);
}Step 3: Open files in binary mode — this is not optional
std::ifstream in(name, std::ios::binary) and ofstream with binary too. In text mode, Windows translates newline bytes on the fly, which silently corrupts images and executables: the file decrypts to something ALMOST right. Binary mode says 'bytes are bytes', which is the only honest mode for a tool that promises 'works on any file'.
Step 4: Refuse to overwrite the input
Compare the input and output filenames before opening anything; if they match, print an error and stop. Opening the output would truncate the input to zero bytes before a single byte is read — the one truly destructive mistake this program can make, guarded by one string comparison.
Step 5: Take the key as a number, use it as a byte
Prompt for a numeric key (say 0–255) and static_cast<char> it. Keys larger than a byte simply wrap, which is fine as long as the SAME key is used to decrypt. Warn the user plainly: wrong key, garbage out — the program cannot tell a wrong key from the right one, because every key produces a valid-looking file.
Step 6: Report bytes processed
Count bytes inside transform() (return the count or use a reference parameter) and print it: 'Encrypted notes.txt -> notes.enc (1284 bytes)'. The count doubles as a verification tool — encrypt and decrypt outputs must be the same size as the input, byte for byte.
Step 7: Test the round-trip on a real file
Copy a text file, encrypt the copy, open it and confirm it is gibberish, decrypt with the same key, and diff the result against the original (fc on Windows, diff on Linux/macOS) — identical output is the only passing grade. Then decrypt once with a WRONG key to see the failure mode your users will meet.
Common mistakes
- Opening files in text mode, so binary files (images, zips) decrypt corrupted — always std::ios::binary on both sides.
- Encrypting with >> and << formatted I/O, which skips whitespace bytes and eats parts of the file.
- Letting input and output be the same filename, truncating the user's only copy before reading it.
Compile and run
g++ -std=c++17 -o encryptor encryptor.cppSample run
$ ./encryptor
Encrypt or Decrypt (E/D):
> E
Input file:
> notes.txt
Output file:
> notes.enc
Numeric key (0-255):
> 42
Encrypted notes.txt -> notes.enc (1284 bytes)
$ ./encryptor
Encrypt or Decrypt (E/D):
> D
Input file:
> notes.enc
Output file:
> notes_restored.txt
Numeric key (0-255):
> 42
Decrypted notes.enc -> notes_restored.txt (1284 bytes)
The restored file is byte-for-byte identical to the original.Try it in your browser
No browser demo is included for this project. Build the C++ version below — that is the real program.