1010import java .security .InvalidKeyException ;
1111import java .security .NoSuchAlgorithmException ;
1212import java .security .SecureRandom ;
13+ import java .security .spec .InvalidKeySpecException ;
1314import java .util .Arrays ;
1415import java .util .Base64 ;
1516import java .util .logging .Level ;
1920import javax .crypto .CipherOutputStream ;
2021import javax .crypto .Mac ;
2122import javax .crypto .NoSuchPaddingException ;
23+ import javax .crypto .SecretKey ;
24+ import javax .crypto .SecretKeyFactory ;
2225import javax .crypto .spec .IvParameterSpec ;
26+ import javax .crypto .spec .PBEKeySpec ;
2327import 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 ("\n Secret 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 );
0 commit comments