Skip to content

Commit 8a6b6ec

Browse files
committed
feat: checks for file integrity and authentication
1 parent e5b8cc7 commit 8a6b6ec

File tree

1 file changed

+51
-7
lines changed

1 file changed

+51
-7
lines changed

src/FileEncryptor.java

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import javax.crypto.Cipher;
1818
import javax.crypto.CipherInputStream;
1919
import javax.crypto.CipherOutputStream;
20+
import javax.crypto.Mac;
2021
import javax.crypto.NoSuchPaddingException;
2122
import javax.crypto.spec.IvParameterSpec;
2223
import javax.crypto.spec.SecretKeySpec;
@@ -31,6 +32,7 @@ public class FileEncryptor {
3132
private static final Logger LOG = Logger.getLogger(FileEncryptor.class.getSimpleName());
3233

3334
private static final String ALGORITHM = "AES";
35+
private static final String HASH_AlGORITHM = "HmacSHA256";
3436
private static final String CIPHER = "AES/CBC/PKCS5PADDING";
3537

3638
public static void main(String[] args) throws Exception {
@@ -116,11 +118,16 @@ public static void encrypt(byte[] key, String inputPath, String outputPath) thro
116118
System.out.println("IV is: " + Base64.getEncoder().encodeToString(initVector));
117119
System.out.print("<---------------------------------------->\n\n");
118120

119-
// Initialize Key, Vector Specfications and the Cipher mode
121+
// Initialize Vector and Keys
120122
IvParameterSpec iv = new IvParameterSpec(initVector);
121123
SecretKeySpec skeySpec = new SecretKeySpec(key, ALGORITHM);
124+
SecretKeySpec macKey = new SecretKeySpec(key, HASH_AlGORITHM);
125+
126+
// Initialize cipher and Mac
122127
Cipher cipher = Cipher.getInstance(CIPHER);
123128
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
129+
Mac hmac = Mac.getInstance(HASH_AlGORITHM);
130+
hmac.init(macKey);
124131

125132
File outputFile = new File(outputPath);
126133
// Create the output file if it doesn't exist
@@ -129,8 +136,10 @@ public static void encrypt(byte[] key, String inputPath, String outputPath) thro
129136
final Path plaintextFile = Paths.get(inputPath);
130137
final Path encryptedFile = Paths.get(outputPath);
131138

139+
byte[] mac = computeMac(hmac, initVector, plaintextFile);
140+
132141
// Write plaintext into ciphertext
133-
if (writeEncryptedFile(plaintextFile, encryptedFile, cipher)) {
142+
if (writeEncryptedFile(plaintextFile, encryptedFile, cipher, mac)) {
134143
LOG.info("Encryption finished, saved at " + encryptedFile);
135144
} else {
136145
LOG.log(Level.WARNING, "Encryption Failed");
@@ -151,11 +160,15 @@ public static void encrypt(byte[] key, String inputPath, String outputPath) thro
151160
* specifications in ENCRYPT mode
152161
* @return boolean True if encryption successful False otherwise
153162
*/
154-
private static boolean writeEncryptedFile(Path inputPath, Path outputPath, Cipher cipher) {
163+
private static boolean writeEncryptedFile(Path inputPath, Path outputPath, Cipher cipher, byte[] mac) {
155164
try (InputStream fin = Files.newInputStream(inputPath);) {
156165

157166
try (FileOutputStream fout = new FileOutputStream(outputPath.toFile());) {
167+
// Write Metadata
168+
fout.write(cipher.getIV().length);
158169
fout.write(cipher.getIV());
170+
fout.write(mac.length);
171+
fout.write(mac);
159172

160173
try (CipherOutputStream cipherOut = new CipherOutputStream(fout, cipher);) {
161174
final byte[] bytes = new byte[1024];
@@ -172,6 +185,20 @@ private static boolean writeEncryptedFile(Path inputPath, Path outputPath, Ciphe
172185
return true;
173186
}
174187

188+
private static byte[] computeMac(Mac hmac, byte[] initVector, Path filePath) {
189+
hmac.update(initVector);
190+
try (InputStream fin = Files.newInputStream(filePath);) {
191+
final byte[] bytes = new byte[1024];
192+
for(int length = fin.read(bytes); length != -1; length = fin.read(bytes)){
193+
hmac.update(bytes, 0, length);
194+
}
195+
} catch (IOException e) {
196+
197+
}
198+
199+
return hmac.doFinal();
200+
}
201+
175202
/**
176203
* This function is invoked when the 'key' option is specified
177204
* in the command line. Generates a random 128 bit key which
@@ -238,14 +265,21 @@ private static boolean writeDecryptedFile(Path inputPath, Path outputPath, byte[
238265
try (InputStream encryptedData = Files.newInputStream(inputPath);){
239266

240267
// Read metadata from the input file
241-
byte[] initVector = new byte[16];
268+
byte[] initVector = new byte[encryptedData.read()];
242269
encryptedData.read(initVector);
243-
244-
// Initialise cipher, IV and key specifications
270+
byte[] givenMac = new byte[encryptedData.read()];
271+
encryptedData.read(givenMac);
272+
273+
// Create key specifications
245274
IvParameterSpec iv = new IvParameterSpec((initVector));
246275
SecretKeySpec skeySpec = new SecretKeySpec(key, ALGORITHM);
276+
SecretKeySpec macKey = new SecretKeySpec(key, HASH_AlGORITHM);
277+
278+
// Initialise cipher and HMac
247279
Cipher cipher = Cipher.getInstance(CIPHER);
248280
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
281+
Mac hmac = Mac.getInstance(HASH_AlGORITHM);
282+
hmac.init(macKey);
249283

250284
// Read cipertext data and write plaintext data
251285
try (CipherInputStream decryptStream = new CipherInputStream(encryptedData, cipher);) {
@@ -255,7 +289,16 @@ private static boolean writeDecryptedFile(Path inputPath, Path outputPath, byte[
255289
decryptedOut.write(bytes, 0, length);
256290
}
257291
}
258-
}
292+
}
293+
294+
// Check authentication and file integerity
295+
byte[] computedMac = computeMac(hmac, initVector, outputPath);
296+
if (!Arrays.equals(givenMac, computedMac)) {
297+
throw new SecurityException("Authentication failed, file may have been tampered with");
298+
} else {
299+
LOG.info("Authentication passed, file integrity maintained");
300+
}
301+
259302
} catch (IOException ex) {
260303
Logger.getLogger(FileEncryptor.class.getName()).log(Level.SEVERE, "IOException caught");
261304
return false;
@@ -266,6 +309,7 @@ private static boolean writeDecryptedFile(Path inputPath, Path outputPath, byte[
266309
} catch (InvalidAlgorithmParameterException e) {
267310
Logger.getLogger(FileEncryptor.class.getName()).log(Level.SEVERE, "InvalidAlgorithmParameterException caught,"
268311
+ " file may have been modified, or invalid key was specified.");
312+
return false;
269313
}
270314
return true;
271315
}

0 commit comments

Comments
 (0)