|
34 | 34 | public class FileEncryptor { |
35 | 35 | private static final Logger LOG = Logger.getLogger(FileEncryptor.class.getSimpleName()); |
36 | 36 |
|
37 | | - private static final String ALGORITHM = "AES"; |
| 37 | + private static final String DEFAULT_ALGORITHM = "AES"; |
| 38 | + private static final int DEFAULT_KEY_LENGTH = 128; |
38 | 39 | private static final String HASH_AlGORITHM = "HmacSHA256"; |
39 | | - private static final String CIPHER = "AES/CBC/PKCS5PADDING"; |
| 40 | + private static final String DEFAULT_CIPHER = "AES/CBC/PKCS5PADDING"; |
40 | 41 | private static final int ITERATION_COUNT = 100000; |
41 | 42 |
|
| 43 | + private static String ALGORITHM; |
| 44 | + private static String CIPHER; |
| 45 | + private static int KEY_LENGTH; |
| 46 | + private static int BLOCKSIZE; |
| 47 | + |
| 48 | + // Error Message |
| 49 | + private static final String ERROR_MSG = "\nValid Encryption command: java FileEncryptor enc [Passoword] [inputFile] [outputFile]\n" |
| 50 | + + "\t\t\t java FileEncryptor enc [Algorithm] [Key length] [Password] [inputFile] [outputFile]\n" |
| 51 | + + "\t\t\t java FileEncryptor enc [Algorithm] [Password] [inputFile] [outputFile]\n" |
| 52 | + + "\t\t\t java FileEncryptor enc [Key length] [Password] [inputFile] [outputFile]\n\n" |
| 53 | + + "Valid Decryption command: java FileEncryptor dec [Password] [inputFile] [outputFile]\n\n" |
| 54 | + + "Valid Info command: java FileEncryptor info [filePath]\n\n" |
| 55 | + + "Valid Key lengths: 128, 448, 192, 32 etc\n\n" |
| 56 | + + "NOTE: The only algorithms accepted are AES and Blowfish\n" |
| 57 | + + "NOTE: Must specify a valid Key length (in bits) with respect to the algorithm specified\n" |
| 58 | + + "NOTE: The default Algorithm being used is " + DEFAULT_ALGORITHM + " and the Default Key Length is " + DEFAULT_KEY_LENGTH + " bits\n" |
| 59 | + + "NOTE: If no Algorithm or Key length is specifed the Default values will be used\n"; |
| 60 | + |
42 | 61 | public static void main(String[] args) throws Exception { |
43 | | - // Error Message |
44 | | - final String validCmdMsg = "Valid Encryption command: java FileEncryptor enc [Password] [inputFile] [outputFile]\n" |
45 | | - + "Valid Decryption command: java FileEncryptor dec [Password] [inputFile] [outputFile]\n"; |
46 | 62 |
|
47 | | - if (args.length < 4) { throw new IllegalArgumentException("Not Enough Argunments specified\n" + validCmdMsg); } |
| 63 | + if (args.length < 2) { throw new IllegalArgumentException("Not Enough Argunments specified\n" + ERROR_MSG); } |
48 | 64 |
|
49 | 65 | // Convert String arguments to char arrays |
50 | 66 | char[][] charArgs = Util.getCharArguments(args); |
51 | 67 |
|
52 | 68 | // Clear String argunments |
53 | 69 | Arrays.fill(args, null); |
54 | 70 |
|
| 71 | + if (Arrays.equals(charArgs[0], "info".toCharArray())) { |
| 72 | + info(new String(charArgs[1])); |
| 73 | + return; |
| 74 | + } |
| 75 | + |
| 76 | + if (charArgs.length < 4) { throw new IllegalArgumentException("Not Enough Argunments specified\n" + ERROR_MSG); } |
| 77 | + |
55 | 78 | // Options Available |
56 | 79 | char[] enc = "enc".toCharArray(); |
57 | 80 | char[] dec = "dec".toCharArray(); |
58 | 81 |
|
59 | 82 | if (!Arrays.equals(charArgs[0], enc) && !Arrays.equals(charArgs[0], dec)) { |
60 | | - throw new IllegalArgumentException("Neither enc (encrypt) or dec (decrypt) option specified\n" + validCmdMsg); |
| 83 | + throw new IllegalArgumentException("Neither enc (encrypt), dec (decrypt) or info option specified\n" + ERROR_MSG); |
61 | 84 | } |
62 | 85 |
|
63 | 86 | if (Arrays.equals(charArgs[0], enc)) { // Encrypt |
64 | | - encrypt(charArgs[1], new String(charArgs[2]), new String(charArgs[3])); |
| 87 | + |
| 88 | + char[] aes = "AES".toCharArray(); |
| 89 | + char [] blowfish = "Blowfish".toCharArray(); |
| 90 | + |
| 91 | + int argIndex = 1; // will get incremented everytime a valid argument is encountered |
| 92 | + |
| 93 | + // If no or incompatiable algorithm argument is specified the Default will be applied |
| 94 | + if (Arrays.equals(charArgs[argIndex], aes) || Arrays.equals(charArgs[argIndex], blowfish)) { |
| 95 | + ALGORITHM = new String(charArgs[1]); |
| 96 | + CIPHER = ALGORITHM + "/CBC/PKCS5PADDING"; |
| 97 | + argIndex++; |
| 98 | + } else { |
| 99 | + ALGORITHM = DEFAULT_ALGORITHM; |
| 100 | + CIPHER = DEFAULT_CIPHER; |
| 101 | + } |
| 102 | + |
| 103 | + // Determine blocksize for the IV |
| 104 | + if (ALGORITHM.equals("AES")) { BLOCKSIZE = 128; } |
| 105 | + if (ALGORITHM.equals("Blowfish")) { BLOCKSIZE = 64; } |
| 106 | + |
| 107 | + // If no Key length specified then the Default will be applied |
| 108 | + try { |
| 109 | + // Perform Key length checks |
| 110 | + int keyLength = Integer.parseInt(new String(charArgs[argIndex])); |
| 111 | + if (keyLength % 8 != 0) { throw new IllegalArgumentException("Invalid Key Length: not divisible by 8"); } |
| 112 | + |
| 113 | + if (ALGORITHM.equals("AES") && keyLength != 128 && keyLength != 192 && keyLength != 256) { |
| 114 | + throw new IllegalArgumentException("Invalid Key Length for AES Algorithm, valid key lengths are 128, 192 or 256 bits"); |
| 115 | + } |
| 116 | + |
| 117 | + if (ALGORITHM.equals("Blowfish") && (keyLength < 32 || keyLength > 448)) { |
| 118 | + throw new IllegalArgumentException("Invalid Key Length for Blowfish Algorithm, valid key lengths are between 32-448 bits"); |
| 119 | + } |
| 120 | + |
| 121 | + KEY_LENGTH = keyLength; |
| 122 | + argIndex++; |
| 123 | + } catch (NumberFormatException e) { |
| 124 | + KEY_LENGTH = DEFAULT_KEY_LENGTH; |
| 125 | + } |
| 126 | + |
| 127 | + // Check if password and/or file paths have been specified |
| 128 | + if (argIndex + 2 >= charArgs.length) { throw new IllegalArgumentException("Not enough arguments specified" + ERROR_MSG); } |
| 129 | + |
| 130 | + encrypt(charArgs[argIndex], new String(charArgs[argIndex + 1]), new String(charArgs[argIndex + 2])); |
65 | 131 |
|
66 | 132 | } else if (Arrays.equals(charArgs[0], dec)) { // Decrypt |
67 | 133 | decrypt(charArgs[1], new String(charArgs[2]), new String(charArgs[3])); |
@@ -97,13 +163,13 @@ public static void main(String[] args) throws Exception { |
97 | 163 | public static void encrypt(char[] password, String inputPath, String outputPath) throws NoSuchAlgorithmException, |
98 | 164 | NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException, InvalidKeySpecException { |
99 | 165 | //Generate vector and salts |
100 | | - final byte[] initVector = new byte[16], salt = new byte[16], macSalt = new byte[16]; |
| 166 | + final byte[] initVector = new byte[BLOCKSIZE/8], salt = new byte[16], macSalt = new byte[16]; |
101 | 167 |
|
102 | 168 | SecureRandom sr = new SecureRandom(); |
103 | 169 | sr.nextBytes(initVector); sr.nextBytes(salt); sr.nextBytes(macSalt); |
104 | 170 |
|
105 | 171 | // Get Keys from password |
106 | | - final byte[] key = generateKey(password, salt, 128); |
| 172 | + final byte[] key = generateKey(password, salt, KEY_LENGTH); |
107 | 173 | final byte[] macKey = generateKey(password, macSalt, 256); |
108 | 174 |
|
109 | 175 | // Password no longer needed |
@@ -158,7 +224,11 @@ private static boolean writeEncryptedFile(Path inputPath, Path outputPath, Ciphe |
158 | 224 |
|
159 | 225 | try (FileOutputStream fout = new FileOutputStream(outputPath.toFile());) { |
160 | 226 | // Write Metadata |
161 | | - fout.write(cipher.getIV()); fout.write(salt); fout.write(macSalt); fout.write(mac); |
| 227 | + final byte[] algorithm = Util.convertCharToByte(ALGORITHM.toCharArray()); |
| 228 | + |
| 229 | + fout.write(BLOCKSIZE); fout.write(KEY_LENGTH/8); fout.write(algorithm.length); |
| 230 | + fout.write(algorithm); fout.write(cipher.getIV()); fout.write(salt); |
| 231 | + fout.write(macSalt); fout.write(mac); |
162 | 232 |
|
163 | 233 | try (CipherOutputStream cipherOut = new CipherOutputStream(fout, cipher);) { |
164 | 234 | final byte[] bytes = new byte[1024]; |
@@ -232,13 +302,14 @@ public static void decrypt(char[] password, String inputPath, String outputPath) |
232 | 302 | private static boolean writeDecryptedFile(Path inputPath, Path outputPath, char[] password) throws NoSuchAlgorithmException, |
233 | 303 | NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException { |
234 | 304 | try (InputStream encryptedData = Files.newInputStream(inputPath);){ |
235 | | - |
236 | 305 | // Read metadata from the input file |
237 | | - final byte[] initVector = new byte[16], salt = new byte[16], macSalt = new byte[16], givenMac = new byte[32]; |
238 | | - |
| 306 | + BLOCKSIZE = encryptedData.read(); KEY_LENGTH = encryptedData.read() * 8; int algoLength = encryptedData.read(); |
| 307 | + ALGORITHM = new String(encryptedData.readNBytes(algoLength)); CIPHER = ALGORITHM + "/CBC/PKCS5PADDING"; |
| 308 | + |
| 309 | + final byte[] initVector = new byte[BLOCKSIZE/8], salt = new byte[16], macSalt = new byte[16], givenMac = new byte[32]; |
239 | 310 | encryptedData.read(initVector); encryptedData.read(salt); encryptedData.read(macSalt); encryptedData.read(givenMac); |
240 | 311 |
|
241 | | - final byte[] key = generateKey(password, salt, 128); |
| 312 | + final byte[] key = generateKey(password, salt, KEY_LENGTH); |
242 | 313 | final byte[] macKey = generateKey(password, macSalt, 256); |
243 | 314 |
|
244 | 315 | // Password no longer needed |
@@ -283,6 +354,10 @@ private static boolean writeDecryptedFile(Path inputPath, Path outputPath, char[ |
283 | 354 | return true; |
284 | 355 | } |
285 | 356 |
|
| 357 | + private static void info(String filepath) { |
| 358 | + System.out.println("INVOKED INFO"); |
| 359 | + } |
| 360 | + |
286 | 361 | /** |
287 | 362 | * Generates a Secret key with a specified password. The password is added with |
288 | 363 | * a salt and iterated multiple times before being hased to increase entropy. |
|
0 commit comments