user1786794
user1786794

Reputation:

Java Encryption With AES and MAC

I am trying to figure out a way to complete my Java coursework. I am expected to encrypt a file but honestly I didn't really understand the instructions. I achieved file encryption and decryption however I have doubts that it does the thing instructions says. The instruction is as following:

The code I have written is like this, it successfully encrypts a text file. This is the whole code for my application.

import java.io.FileNotFoundException;
import java.io.*;
import java.util.Scanner;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.AlgorithmParameters;
import java.security.SecureRandom;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class AESFileEncryption {

/*public AESFileEncryption(String nameoffile){

}
public String FileReturn(String filename){
    String fl = filename;       
    return fl;      
}*/

public static void main(String[] args) throws Exception {

    File f = new File("plainfile.txt");
    File g = new File("plainfile.txt.8102");
    File fl = new File("plainfile.txt.8102");

    if(g.exists() && !g.isDirectory()){
        System.out.println("The file is already encrypted...");
        String fname = fl.getAbsolutePath();
        System.out.print("Absolute Encrypted File Pathname => "+ fname);
        System.exit(0);
    }       
    else if(f.exists() && !f.isDirectory()) { 
         System.out.println(" The file is found.The encryption process is going to begin...");

    }       
    else{
         System.out.println(" The file is missing!!!!");
         System.exit(0);
    }

    // file to be encrypted
    FileInputStream inFile = new FileInputStream("plainfile.txt");       

    // encrypted file
    FileOutputStream outFile = new FileOutputStream("plainfile.txt.8102");


    // password to encrypt the file
    Scanner scan= new Scanner(System.in);
    System.out.println("Enter the password : => ");
    String password= scan.nextLine();

    //String password = "javapapers";

    // password, iv and salt should be transferred to the other end
    // in a secure manner

    // salt is used for encoding
    // writing it to a file
    // salt should be transferred to the recipient securely
    // for decryption
    byte[] salt = new byte[8];
    SecureRandom secureRandom = new SecureRandom();
    secureRandom.nextBytes(salt);
    FileOutputStream saltOutFile = new FileOutputStream("salt.enc");
    saltOutFile.write(salt);
    saltOutFile.close();

    SecretKeyFactory factory = SecretKeyFactory
            .getInstance("PBKDF2WithHmacSHA1");
    KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 65536,
            256);
    SecretKey secretKey = factory.generateSecret(keySpec);
    SecretKey secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    //
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();

    // iv adds randomness to the text and just makes the mechanism more
    // secure
    // used while initializing the cipher
    // file to store the iv
    FileOutputStream ivOutFile = new FileOutputStream("iv.enc");
    byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
    ivOutFile.write(iv);
    ivOutFile.close();

    //file encryption
    byte[] input = new byte[64];
    int bytesRead;

    while ((bytesRead = inFile.read(input)) != -1) {
        byte[] output = cipher.update(input, 0, bytesRead);
        if (output != null)
            outFile.write(output);
    }

    byte[] output = cipher.doFinal();
    if (output != null)
        outFile.write(output);

    inFile.close();
    outFile.flush();
    outFile.close();

    System.out.println("File Encrypted.");

    }

}

Upvotes: 2

Views: 9472

Answers (1)

Artjom B.
Artjom B.

Reputation: 61892

The instructor means that HMAC should be applied to create an authentication tag for the ciphertext. This is called encrypt-then-MAC. HMAC is a keyed hash function which provides an authenticity (and integrity since authenticity without integrity doesn't mean anything) check for the recipient who has the correct key. Since it is essentially a hash function, it works by updating the internal state.

Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec macKey = new SecretKeySpec(macKeyBytes, "HmacSHA1");
mac.init(macKey);
mac.update(iv); // update for IV
...
mac.update(output); // update for each ciphertext chunk
...
byte[] authTag = mac.doFinal();
outfile.write(authTag); // at the very end

One problem remains and that is the generation of the macKeyBytes. It should not be the same as the encryption key that you generated from the password through PBKDF2. You should use the key that you generated to derive the encryption and the MAC key separately. This is usually done with HKDF, but you could also use HMAC again for that. Pseudo-code:

byte[] encKeyBytes = hmacSha256(key, "Encryption");
byte[] macKeyBytes = hmacSha256(key, "Authentication");

The instruction doesn't say anything about the key or password, so I'm going to assume it should have been a static key (for testing purposes). The way you're using PBKDF2 is ok, but the salt also must be written to the file. Otherwise, the recipient won't be able to derive the same key without the random salt. Also, the salt should be probably 16 bytes long.


Another problem with your current code is that you're writing the IV into a separate file. Don't do that. Just write the IV to the beginning of the ciphertext file. The IV has the same size as the block in CBC mode and AES has a fixed block size of 16 bytes. The recipient will always know how many bytes are to be read for the IV.

Upvotes: 5

Related Questions