1+ import java .io .File ;
12import java .io .IOException ;
23import java .io .InputStream ;
34import java .io .OutputStream ;
45import java .nio .file .Files ;
56import java .nio .file .Path ;
7+ import java .nio .file .Paths ;
68import java .security .InvalidAlgorithmParameterException ;
79import java .security .InvalidKeyException ;
810import java .security .NoSuchAlgorithmException ;
911import java .security .SecureRandom ;
12+ import java .util .Arrays ;
13+ import java .util .Base64 ;
1014import java .util .logging .Level ;
1115import java .util .logging .Logger ;
1216import javax .crypto .Cipher ;
1620import javax .crypto .spec .IvParameterSpec ;
1721import javax .crypto .spec .SecretKeySpec ;
1822
23+
1924/**
2025 *
2126 * @author Erik Costlow
27+ * @author Pravin Modotholi
2228 */
2329public class FileEncryptor {
2430 private static final Logger LOG = Logger .getLogger (FileEncryptor .class .getSimpleName ());
2531
2632 private static final String ALGORITHM = "AES" ;
2733 private static final String CIPHER = "AES/CBC/PKCS5PADDING" ;
2834
29- public static void main (String [] args ) throws NoSuchAlgorithmException , NoSuchPaddingException , InvalidKeyException , InvalidAlgorithmParameterException , IOException {
35+ public static void main (String [] args ) throws Exception {
36+ // Error Message
37+ final String validCmdMsg = "Valid Encryption command: java FileEncryptor enc [inputFile] [outputFile]\n "
38+ + "Valid Decryption command: java FileEncryptor dec [Key] [Vector] [inputFile] [outputFile]" ;
39+
40+ if (args .length < 3 ) { throw new IllegalArgumentException ("Not Enough Argunments specified\n " + validCmdMsg ); }
41+
42+ // Convert String arguments to char arrays
43+ char [][] charArgs = Util .getCharArgunments (args );
44+
45+ // Clear String argunments
46+ Arrays .fill (args , null );
47+
48+ if (Arrays .equals (charArgs [0 ], "enc" .toCharArray ())) { // Encrypt
49+ encrypt (new String (charArgs [1 ]), new String (charArgs [2 ]));
3050
51+ } else if (Arrays .equals (charArgs [0 ], "dec" .toCharArray ())) { // Decrypt
52+
53+ if (charArgs .length < 5 ) { throw new IllegalArgumentException ("Not Enough Argunments Provided for Decryption\n " + validCmdMsg ); }
54+
55+ // Decode the Base64 argunments
56+ byte [] key = Base64 .getDecoder ().decode (Util .convertCharToByte (charArgs [1 ]));
57+ byte [] initVector = Base64 .getDecoder ().decode (Util .convertCharToByte (charArgs [2 ]));
58+
59+ decrypt (key , initVector , new String (charArgs [3 ]), new String (charArgs [4 ]));
60+
61+ // Tear Down, clear arrays
62+ Arrays .fill (key , (byte ) 0 );
63+ Arrays .fill (initVector , (byte ) 0 );
64+ key = null ; initVector = null ;
65+
66+ for (int i = 0 ; i < charArgs .length ; i ++) {
67+ Arrays .fill (charArgs [i ], '\0' );
68+ }
69+ charArgs = null ;
70+
71+ } else {
72+ throw new IllegalArgumentException ("Neither enc (encrypt) or dec (decrypt) option specified\n " + validCmdMsg );
73+ }
74+ }
75+
76+ /**
77+ * Encrypts a plain text input file by outputing an encrypted version. It does this
78+ * generating a 128 bit secret key and initialisation vector which are used as
79+ * specifications during the file encryption process.
80+ *
81+ * @param inputPath - A String specifying the Input path of the plaintext file
82+ * @param outputPath - A String specifying the Ouput path of the ciphertext file
83+ * @throws NoSuchAlgorithmException
84+ * @throws NoSuchPaddingException
85+ * @throws InvalidKeyException
86+ * @throws InvalidAlgorithmParameterException
87+ * @throws IOException
88+ */
89+ public static void encrypt (String inputPath , String outputPath ) throws NoSuchAlgorithmException ,
90+ NoSuchPaddingException , InvalidKeyException , InvalidAlgorithmParameterException , IOException {
3191 //This snippet is literally copied from SymmetrixExample
3292 SecureRandom sr = new SecureRandom ();
3393 byte [] key = new byte [16 ];
3494 sr .nextBytes (key ); // 128 bit key
3595 byte [] initVector = new byte [16 ];
3696 sr .nextBytes (initVector ); // 16 bytes IV
37- System .out .println ("Random key=" + Util .bytesToHex (key ));
38- System .out .println ("initVector=" + Util .bytesToHex (initVector ));
97+
98+ // Display the Base64 encoded versions of Key and Vector
99+ System .out .print ("\n <---------------------------------------->\n " );
100+ System .out .println ("Secret Key is: " + Base64 .getEncoder ().encodeToString (key ));
101+ System .out .println ("IV is: " + Base64 .getEncoder ().encodeToString (initVector ));
102+ System .out .print ("<---------------------------------------->\n \n " );
103+
104+ // Initialize Key and Vector Specfications and the Cipher mode
39105 IvParameterSpec iv = new IvParameterSpec (initVector );
40106 SecretKeySpec skeySpec = new SecretKeySpec (key , ALGORITHM );
41107 Cipher cipher = Cipher .getInstance (CIPHER );
42108 cipher .init (Cipher .ENCRYPT_MODE , skeySpec , iv );
109+
110+ File outputFile = new File (outputPath );
111+ // Create the output file if it doesn't exist
112+ if (!outputFile .exists ()) { outputFile .createNewFile (); }
43113
44- //Look for files here
45- final Path tempDir = Files .createTempDirectory ("packt-crypto" );
46-
47- final Path encryptedPath = tempDir .resolve ("1 - Encrypting and Decrypting files.pptx.encrypted" );
48- try (InputStream fin = FileEncryptor .class .getResourceAsStream ("1 - Encrypting and Decrypting files.pptx" );
49- OutputStream fout = Files .newOutputStream (encryptedPath );
50- CipherOutputStream cipherOut = new CipherOutputStream (fout , cipher ) {
114+ final Path plaintextFile = Paths .get (inputPath );
115+ final Path encryptedFile = Paths .get (outputPath );
116+
117+ // Write plaintext into ciphertext
118+ if (writeEncryptedFile (plaintextFile , encryptedFile , cipher )) {
119+ LOG .info ("Encryption finished, saved at " + encryptedFile );
120+ } else {
121+ LOG .log (Level .WARNING , "Encryption Failed, Ensure Valid File Paths are specified" );
122+ }
123+ }
124+
125+ /**
126+ * Writes an encrypted version of the input file, into the output file.
127+ * Uses a FileInputStream to read the plaintext file and wraps the OutputStream
128+ * with a CipherOutStream to write an encrypted version of the plaintext file.
129+ * Returns True if the encryption writing was successfull, False otherwise.
130+ *
131+ * @param inputPath Path The file path of the input file (plaintext)
132+ * @param outputPath Path The file path of the output file (ciphertext)
133+ * @param cipher Cipher The cipher instance initialized with the appropriate
134+ * specifications in ENCRYPT mode
135+ * @return boolean True if encryption successful False otherwise
136+ */
137+ private static boolean writeEncryptedFile (Path inputPath , Path outputPath , Cipher cipher ) {
138+ try (InputStream fin = Files .newInputStream (inputPath );
139+ OutputStream fout = Files .newOutputStream (outputPath );
140+ CipherOutputStream cipherOut = new CipherOutputStream (fout , cipher ) {
51141 }) {
52142 final byte [] bytes = new byte [1024 ];
53- for (int length = fin .read (bytes ); length != -1 ; length = fin .read (bytes )){
143+ for (int length = fin .read (bytes ); length != -1 ; length = fin .read (bytes )){
54144 cipherOut .write (bytes , 0 , length );
55145 }
56146 } catch (IOException e ) {
57- LOG .log (Level .INFO , "Unable to encrypt" , e );
147+ LOG .log (Level .INFO , "Unable to encrypt" );
148+ return false ;
58149 }
59-
60- LOG .info ("Encryption finished, saved at " + encryptedPath );
61-
150+
151+ return true ;
152+ }
153+
154+ /**
155+ * Decrypts a given cipertext file into its original plaintext form.
156+ * A successful decryption occurs when provided with the right key and
157+ * initialisation vector to create the Cipher specifications required
158+ * for decryption. Will overwrite the resultant output file if it
159+ * already exists.
160+ *
161+ * @param key byte[] - The Key used to originally encrypt the input file
162+ * @param initVector byte[] - The initialisation vector originally used for encryption
163+ * @param inputPath String - The input file path (encrypted document)
164+ * @param outputPath String - The file path of the resultant decrypted text
165+ * @throws NoSuchAlgorithmException
166+ * @throws NoSuchPaddingException
167+ * @throws InvalidKeyException
168+ * @throws InvalidAlgorithmParameterException
169+ * @throws IOException
170+ */
171+ public static void decrypt (byte [] key , byte [] initVector , String inputPath , String outputPath ) throws NoSuchAlgorithmException ,
172+ NoSuchPaddingException , InvalidKeyException , InvalidAlgorithmParameterException , IOException {
173+ // Initialize Key and Vector Specifications and the Cipher Mode
174+ IvParameterSpec iv = new IvParameterSpec (initVector );
175+ SecretKeySpec skeySpec = new SecretKeySpec (key , ALGORITHM );
176+ Cipher cipher = Cipher .getInstance (CIPHER );
62177 cipher .init (Cipher .DECRYPT_MODE , skeySpec , iv );
63- final Path decryptedPath = tempDir .resolve ("1 - Encrypting and Decrypting files_decrypted.pptx" );
64- try (InputStream encryptedData = Files .newInputStream (encryptedPath );
65- CipherInputStream decryptStream = new CipherInputStream (encryptedData , cipher );
66- OutputStream decryptedOut = Files .newOutputStream (decryptedPath )){
67- final byte [] bytes = new byte [1024 ];
68- for (int length =decryptStream .read (bytes ); length !=-1 ; length = decryptStream .read (bytes )){
69- decryptedOut .write (bytes , 0 , length );
70- }
178+
179+ File outputFile = new File (outputPath );
180+ // Create a new Decrypted file if it doesn't exist
181+ if (!outputFile .exists ()) { outputFile .createNewFile (); }
182+
183+ final Path encryptedFile = Paths .get (inputPath );
184+ final Path decryptedFile = Paths .get (outputPath );
185+
186+ if (writeDecryptedFile (encryptedFile , decryptedFile , cipher )) {
187+ LOG .info ("Decryption complete, open " + decryptedFile );
188+ } else {
189+ LOG .log (Level .SEVERE , "Ensure the correct Key, Vector, and Files pahts are specified" );
190+ }
191+ }
192+
193+ /**
194+ * Reads an encrypted file by wrapping an InputStream with a CipherInputStream
195+ * The encrypted files gets decrypted and written out to the output file.
196+ * For a successful decryption the Cipher new to be initialized in DECRYPT mode
197+ * with the correct key and vector specifications.
198+ *
199+ * @param inputPath Path The input file path (encrypted file)
200+ * @param outputPath Path The output file path (decrypted file)
201+ * @param cipher Cipher The cipher instance initialized with the appropriate
202+ * specifications in DECRYPT mode
203+ * @return boolean True if Decryption is successful False otherwise
204+ */
205+ private static boolean writeDecryptedFile (Path inputPath , Path outputPath , Cipher cipher ) {
206+ try (InputStream encryptedData = Files .newInputStream (inputPath );
207+ CipherInputStream decryptStream = new CipherInputStream (encryptedData , cipher );
208+ OutputStream decryptedOut = Files .newOutputStream (outputPath )) {
209+
210+ final byte [] bytes = new byte [1024 ];
211+ for (int length =decryptStream .read (bytes ); length !=-1 ; length = decryptStream .read (bytes )){
212+ decryptedOut .write (bytes , 0 , length );
213+ }
214+
71215 } catch (IOException ex ) {
72- Logger .getLogger (FileEncryptor .class .getName ()).log (Level .SEVERE , "Unable to decrypt" , ex );
216+ Logger .getLogger (FileEncryptor .class .getName ()).log (Level .SEVERE , "Unable to decrypt" );
217+ return false ;
73218 }
74-
75- LOG .info ("Decryption complete, open " + decryptedPath );
219+ return true ;
76220 }
77221}
0 commit comments