Skip to content

Commit f978a65

Browse files
committed
feat: password based encryption and decryption implemented
#16 #17 #18
1 parent c207690 commit f978a65

File tree

2 files changed

+50
-52
lines changed

2 files changed

+50
-52
lines changed

src/FileEncryptor.java

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.security.InvalidKeyException;
1111
import java.security.NoSuchAlgorithmException;
1212
import java.security.SecureRandom;
13+
import java.security.spec.InvalidKeySpecException;
1314
import java.util.Arrays;
1415
import java.util.Base64;
1516
import java.util.logging.Level;
@@ -19,7 +20,10 @@
1920
import javax.crypto.CipherOutputStream;
2021
import javax.crypto.Mac;
2122
import javax.crypto.NoSuchPaddingException;
23+
import javax.crypto.SecretKey;
24+
import javax.crypto.SecretKeyFactory;
2225
import javax.crypto.spec.IvParameterSpec;
26+
import javax.crypto.spec.PBEKeySpec;
2327
import javax.crypto.spec.SecretKeySpec;
2428

2529

@@ -34,30 +38,21 @@ public class FileEncryptor {
3438
private static final String ALGORITHM = "AES";
3539
private static final String HASH_AlGORITHM = "HmacSHA256";
3640
private static final String CIPHER = "AES/CBC/PKCS5PADDING";
41+
private static final int ITERATION_COUNT = 1000 * 128;
3742

3843
public static void main(String[] args) throws Exception {
3944
// Error Message
40-
final String validCmdMsg = "Valid Encryption command: java FileEncryptor enc [Key] [inputFile] [outputFile]\n"
41-
+ "Valid Decryption command: java FileEncryptor dec [Key] [inputFile] [outputFile]\n"
42-
+ "Valid Key generation command: java FileEncryptor key\n"
43-
+ "NOTE: The key specified must be Base64 Encoded";
45+
final String validCmdMsg = "Valid Encryption command: java FileEncryptor enc [Password] [inputFile] [outputFile]\n"
46+
+ "Valid Decryption command: java FileEncryptor dec [Password] [inputFile] [outputFile]\n";
4447

45-
if (args.length < 1) { throw new IllegalArgumentException("Not Enough Argunments specified\n" + validCmdMsg); }
48+
if (args.length < 4) { throw new IllegalArgumentException("Not Enough Argunments specified\n" + validCmdMsg); }
4649

4750
// Convert String arguments to char arrays
4851
char[][] charArgs = Util.getCharArguments(args);
4952

5053
// Clear String argunments
5154
Arrays.fill(args, null);
5255

53-
// Generate and display key, for testing and marking purposes
54-
if (Arrays.equals(charArgs[0], "key".toCharArray())) {
55-
generateKey();
56-
return;
57-
}
58-
59-
if (charArgs.length < 4) { throw new IllegalArgumentException("Not Enough Argunments Provided\n" + validCmdMsg ); }
60-
6156
// Options Available
6257
char[] enc = "enc".toCharArray();
6358
char[] dec = "dec".toCharArray();
@@ -66,29 +61,29 @@ public static void main(String[] args) throws Exception {
6661
throw new IllegalArgumentException("Neither enc (encrypt) or dec (decrypt) option specified\n" + validCmdMsg);
6762
}
6863

69-
byte[] key = new byte[16];
70-
try {
71-
key = Base64.getDecoder().decode(Util.convertCharToByte(charArgs[1]));
72-
} catch (IllegalArgumentException e) {
73-
throw new IllegalArgumentException("Key provided must be Base64 encoded\n" + validCmdMsg);
74-
}
75-
7664
if (Arrays.equals(charArgs[0], enc)) { // Encrypt
77-
encrypt(key, new String(charArgs[2]), new String(charArgs[3]));
65+
encrypt(charArgs[1], new String(charArgs[2]), new String(charArgs[3]));
7866

7967
} else if (Arrays.equals(charArgs[0], dec)) { // Decrypt
80-
decrypt(key, new String(charArgs[2]), new String(charArgs[3]));
68+
decrypt(charArgs[1], new String(charArgs[2]), new String(charArgs[3]));
8169

8270
}
8371

8472
// Tear Down, clear arrays
85-
Arrays.fill(key, (byte) 0);
8673
Arrays.fill(enc, '\0'); Arrays.fill(dec, '\0');
8774

8875
for (int i = 0; i < charArgs.length; i++) {
8976
Arrays.fill(charArgs[i], '\0');
9077
}
91-
charArgs = null; key = null; dec = null; enc = null;
78+
charArgs = null; dec = null; enc = null;
79+
}
80+
81+
private static byte[] generateKey(char[] password, byte[] salt, int keyLength) throws NoSuchAlgorithmException,
82+
InvalidKeySpecException {
83+
PBEKeySpec passwordKeySpec = new PBEKeySpec(password, salt, ITERATION_COUNT, keyLength);
84+
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
85+
SecretKey secretKey = keyFactory.generateSecret(passwordKeySpec);
86+
return secretKey.getEncoded();
9287
}
9388

9489
/**
@@ -106,13 +101,19 @@ public static void main(String[] args) throws Exception {
106101
* @throws InvalidKeyException
107102
* @throws InvalidAlgorithmParameterException
108103
* @throws IOException
104+
* @throws InvalidKeySpecException
109105
*/
110-
public static void encrypt(byte[] key, String inputPath, String outputPath) throws NoSuchAlgorithmException,
111-
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException {
112-
//Generate Initilisation Vector
113-
SecureRandom sr = new SecureRandom();
106+
public static void encrypt(char[] password, String inputPath, String outputPath) throws NoSuchAlgorithmException,
107+
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException, InvalidKeySpecException {
108+
//Generate vector and salts
114109
final byte[] initVector = new byte[16];
115-
sr.nextBytes(initVector); // 16 bytes IV
110+
final byte[] salt = new byte[16];
111+
112+
SecureRandom sr = new SecureRandom();
113+
sr.nextBytes(initVector);
114+
sr.nextBytes(salt);
115+
116+
final byte[] key = generateKey(password, salt, 128);
116117

117118
// Initialize Vector and Keys
118119
IvParameterSpec iv = new IvParameterSpec(initVector);
@@ -122,6 +123,7 @@ public static void encrypt(byte[] key, String inputPath, String outputPath) thro
122123
// Initialize cipher and Mac
123124
Cipher cipher = Cipher.getInstance(CIPHER);
124125
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
126+
125127
Mac hmac = Mac.getInstance(HASH_AlGORITHM);
126128
hmac.init(macKey);
127129

@@ -136,14 +138,15 @@ public static void encrypt(byte[] key, String inputPath, String outputPath) thro
136138
hmac.update(initVector);
137139
final byte[] mac = computeMac(hmac, plaintextFile);
138140

139-
// Display the Base64 encoded versions of Vector and computed mac
141+
// Display the Base64 encoded versions of Key, Vector and computed mac
140142
System.out.print("\n<---------------------------------------->\n");
143+
System.out.println("Secret Key is: " + Base64.getEncoder().encodeToString(key));
141144
System.out.println("IV is: " + Base64.getEncoder().encodeToString(initVector));
142145
System.out.println("Computed Mac: " + Base64.getEncoder().encodeToString(mac));
143146
System.out.print("<---------------------------------------->\n\n");
144147

145148
// Write plaintext into ciphertext
146-
if (writeEncryptedFile(plaintextFile, encryptedFile, cipher, mac)) {
149+
if (writeEncryptedFile(plaintextFile, encryptedFile, cipher, salt, mac)) {
147150
LOG.info("Encryption finished, saved at " + encryptedFile);
148151
} else {
149152
LOG.log(Level.WARNING, "Encryption Failed");
@@ -164,12 +167,13 @@ public static void encrypt(byte[] key, String inputPath, String outputPath) thro
164167
* specifications in ENCRYPT mode
165168
* @return boolean True if encryption successful False otherwise
166169
*/
167-
private static boolean writeEncryptedFile(Path inputPath, Path outputPath, Cipher cipher, byte[] mac) {
170+
private static boolean writeEncryptedFile(Path inputPath, Path outputPath, Cipher cipher, byte[] salt, byte[] mac) {
168171
try (InputStream fin = Files.newInputStream(inputPath);) {
169172

170173
try (FileOutputStream fout = new FileOutputStream(outputPath.toFile());) {
171174
// Write Metadata
172175
fout.write(cipher.getIV());
176+
fout.write(salt);
173177
fout.write(mac);
174178

175179
try (CipherOutputStream cipherOut = new CipherOutputStream(fout, cipher);) {
@@ -211,19 +215,6 @@ private static byte[] computeMac(Mac hmac, Path filePath) {
211215
return hmac.doFinal();
212216
}
213217

214-
/**
215-
* This function is invoked when the 'key' option is specified
216-
* in the command line. Generates a random 128 bit key which
217-
* gets printed out in Base64 encoded form to the command line.
218-
* For marking purposes and ease of testing for Part 2.
219-
*/
220-
private static void generateKey() {
221-
SecureRandom sr = new SecureRandom();
222-
byte[] key = new byte[16];
223-
sr.nextBytes(key);
224-
System.out.println("\nSecret Key is: " + Base64.getEncoder().encodeToString(key) + "\n");
225-
}
226-
227218
/**
228219
* Decrypts a given cipertext file into its original plaintext form.
229220
* A successful decryption occurs when provided with the right key
@@ -238,9 +229,11 @@ private static void generateKey() {
238229
* @throws InvalidKeyException
239230
* @throws InvalidAlgorithmParameterException
240231
* @throws IOException
232+
* @throws InvalidKeySpecException
241233
*/
242-
public static void decrypt(byte[] key, String inputPath, String outputPath) throws IOException,
243-
NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException{
234+
public static void decrypt(char[] password, String inputPath, String outputPath) throws IOException,
235+
NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
236+
InvalidKeySpecException{
244237

245238
File outputFile = new File(outputPath);
246239
// Create a new Output file if it doesn't exist
@@ -249,7 +242,7 @@ public static void decrypt(byte[] key, String inputPath, String outputPath) thro
249242
final Path encryptedFile = Paths.get(inputPath);
250243
final Path decryptedFile = Paths.get(outputPath);
251244

252-
if (writeDecryptedFile(encryptedFile, decryptedFile, key)) {
245+
if (writeDecryptedFile(encryptedFile, decryptedFile, password)) {
253246
LOG.info("Decryption complete, open " + decryptedFile);
254247
} else {
255248
LOG.log(Level.SEVERE, "Decryption failed: Ensure the correct Key and Files paths are specified");
@@ -267,23 +260,28 @@ public static void decrypt(byte[] key, String inputPath, String outputPath) thro
267260
*
268261
* @param inputPath Path The input file path (encrypted file)
269262
* @param outputPath Path The output file path (decrypted file)
270-
* @param key byte[] The secret key which will be used for decryption
263+
* @param password char[] The password entered by the user
271264
* @return boolean True if Decryption is successful False otherwise
272265
* @throws NoSuchPaddingException
273266
* @throws NoSuchAlgorithmException
274267
* @throws InvalidKeyException
275268
* @throws InvalidAlgorithmParameterException
269+
* @throws InvalidKeySpecException
276270
*/
277-
private static boolean writeDecryptedFile(Path inputPath, Path outputPath, byte[] key) throws NoSuchAlgorithmException,
278-
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
271+
private static boolean writeDecryptedFile(Path inputPath, Path outputPath, char[] password) throws NoSuchAlgorithmException,
272+
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException {
279273
try (InputStream encryptedData = Files.newInputStream(inputPath);){
280274

281275
// Read metadata from the input file
282276
final byte[] initVector = new byte[16];
277+
final byte[] salt = new byte[16];
283278
final byte[] givenMac = new byte[32];
284279

285280
encryptedData.read(initVector);
281+
encryptedData.read(salt);
286282
encryptedData.read(givenMac);
283+
284+
final byte[] key = generateKey(password, salt, 128);
287285

288286
// Create key specifications
289287
IvParameterSpec iv = new IvParameterSpec(initVector);

src/Util.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static byte[] convertCharToByte(char[] chars) {
3434
}
3535

3636
/**
37-
* Converts an arry of Strings into a 2-Dimensional array of
37+
* Converts an array of Strings into a 2-Dimensional array of
3838
* characters, each row representing the String as a char array
3939
* and each column in a row representing the individual char value
4040
*

0 commit comments

Comments
 (0)