You will build a Java program that can encrypt and decrypt text files using a randomly generated substitution cipher. Each time your program encrypts a file, it produces a unique, random character mapping — meaning the same input encrypted twice will produce two completely different outputs.
Your program saves this mapping as a key file, which is later required to decrypt. This is the fundamental idea behind symmetric-key encryption: whoever holds the key can lock and unlock the message; everyone else sees gibberish.
.txt filesHashMap<Character, Character> as a bidirectional lookup tableYour program should be run from the command line in two modes:
$ java DynamicCipher encrypt <input_file>
Behavior:
<input_file> (a .txt file of arbitrary length).<input_file>.encmessage.txt → message.txt.enc).<input_file>.keymessage.txt → message.txt.key).$ java DynamicCipher decrypt <encrypted_file> <key_file>
Behavior:
'A' → 'x' in the encryption map, then 'x' → 'A' in the decryption map).<encrypted_file>.decmessage.txt.enc → message.txt.enc.dec).
Your cipher must handle all printable ASCII characters — codes 32 (space) through
126 (~). That is 95 characters total. Characters outside this range (newlines, tabs, etc.)
should be passed through unchanged.
Your mapping must be a true permutation — every character in the range maps to exactly one unique character, with no repeats. The simplest approach:
char[] of all 95 printable ASCII characters in order.Collections.shuffle() on an ArrayList<Character>).Your key file must store enough information for the decrypt mode to perfectly reconstruct the mapping. Here are some approaches to consider (pick one, or invent your own):
<original><delimiter><mapped>Whatever format you choose, document it in a comment at the top of your source code.
Your program should print a helpful error message and exit cleanly if:
encrypt or decryptWork through these in order. Test each one before moving on.
Create a HashMap<Character, Character> with a simple, fixed mapping
(e.g., shift every letter by 1). Encrypt and decrypt a short test file. Verify the decrypted
output matches the original exactly.
Replace the hardcoded map with a randomly generated permutation. Print the mapping to the console so you can visually verify that every character maps to a unique target.
Design your key file format. Write the key to a file during encryption. Read it back during decryption and verify that the reconstructed map matches the original.
Wire everything together with command-line argument parsing. Test the full round trip:
$ java DynamicCipher encrypt secret.txt
$ java DynamicCipher decrypt secret.txt.enc secret.txt.key
Then diff the original against the decrypted file — they should be identical.
Use these test cases to verify correctness:
| Test | What to Check |
|---|---|
| Short file (1 sentence) | Basic round-trip: original == decrypted |
| Empty file | Program handles gracefully, produces empty output |
| All 95 printable ASCII chars | Every character encrypts and decrypts correctly |
| File with newlines and tabs | Non-printable characters pass through unchanged |
| Encrypt same file twice | Two different .enc files are produced (different keys) |
| Decrypt with wrong key | Output should be garbled (not crash) |
Create a file called test_input.txt with the following contents:
The quick brown fox jumps over the lazy dog.
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789
Special chars: !@#$%^&*()_+-=[]{}|;':",.<>?/`~
This covers uppercase, lowercase, digits, punctuation, and spaces — a good stress test for your full character range.
If you finish the base assignment, try one or more of these:
After substitution, apply a Vigenère cipher using a user-provided password. The password is not saved anywhere — the user must remember it. Now decryption requires both the key file and the correct password.
Calculate and print the total number of possible keys your cipher could generate (hint: it is 95!). Research why this makes brute force infeasible, and write a brief explanation as a comment in your code.
Write a third mode, analyze, that takes an encrypted file without a key and
attempts to crack it using English letter frequency analysis. How long does the file need to be
before this works reliably? This is a great introduction to why substitution ciphers are
historically breakable.
DynamicCipher.java — complete, compiling source codeencrypt and decrypt modes via command line