1

I am trying to use Java's cryptography library to implement AES encryption using GCM mode (because it's an AEAD scheme and I'd like to make sure the data arrives intact) but I'd like to use a password for user friendliness. Getting the program to compile without errors was an difficult until I slammed my head against a brick wall a few times and read this other stackoverflow response.

Now, unfortunately the program compiles but the ciphertext it returns is a fraction of the amount of bytes in the message (I suspect it's only the header that GCM uses and the actual message isn't being written at all). Can somebody please lend a hand?

Here are some program variables:

private static final byte[] SALT = {(byte)0x3b,(byte)0x12,(byte)0x44,(byte)0x61,
        (byte)0xec,(byte)0xc0,(byte)0xa1,(byte)0x82,
        (byte)0x4d, (byte)0x97, (byte)0x4d, (byte)0x3c,
        (byte)0x57, (byte)0xd9, (byte)0x94, (byte)0x52};

private static final String CIPHER_ALG = "AES/GCM/PKCS5Padding";
private static final String SEC_RANDOM_ALG = "SHA1PRNG";
private static final String S_KEY_FACTORY_ALG = "PBKDF2WithHmacSHA1";

private static final int AES_KEY_SIZE = 128; //num bits
private static final int GCM_NONCE_LENGTH = 16; //num bytes
private static final int GCM_TAG_LENGTH = 16; //num bytes
private static final int KEY_SPEC_ROUNDS = 65536;

And here is the encrypt method:

//FIXME somehow the cipher is truncating the message or something
private byte[] encrypt(File file, String password) throws IOException{

    Path path = Paths.get(file.toURI());
    byte[] messageBytes = Files.readAllBytes(path);
    //Message bytes: 3253
    System.out.printf("Message bytes: %d%n", messageBytes.length);
    byte[] messageName = path.getFileName().toString().getBytes();

    byte[] cipherBytes = {};
    byte[] iv = {};
    try{

        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(S_KEY_FACTORY_ALG);
        PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), SALT, KEY_SPEC_ROUNDS, AES_KEY_SIZE);
        SecretKey key = keyFactory.generateSecret(pbeKeySpec);
        System.out.printf("Secret key take 1: " + Arrays.toString(key.getEncoded()) + "%n");

        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getEncoded(), "AES");

        //FIXME this is not encrypting the message correctly
        Cipher cipher = Cipher.getInstance(CIPHER_ALG);
        final byte[] nonce = new byte[GCM_NONCE_LENGTH];
        SecureRandom random = SecureRandom.getInstanceStrong();
        random.nextBytes(nonce);
        GCMParameterSpec paramSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
        iv = paramSpec.getIV();

        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
        cipher.updateAAD(messageName);
        cipher.update(messageBytes);
        cipherBytes = cipher.doFinal();

    }catch(NoSuchAlgorithmException e){
        e.printStackTrace();
    }catch(NoSuchPaddingException e){
        e.printStackTrace();
    }catch(InvalidKeyException e){
        e.printStackTrace();
    }catch(InvalidAlgorithmParameterException e){
        e.printStackTrace();
    }catch(InvalidKeySpecException e){
        e.printStackTrace();
    }catch(BadPaddingException e){
        e.printStackTrace();
    }catch(IllegalBlockSizeException e){
        e.printStackTrace();
    }

    //IV bytes: 16
    System.out.printf("IV bytes: %d%n", iv.length);
    //Cipher bytes: 21
    System.out.printf("Cipher bytes: %d%n", cipherBytes.length);
    //Cipher bytes: [121, 68, 7, -69, -35, -9, -83, 101, -60, -80, 42, 59, -67, 126, 18, -82, 79, -60, 34, -125, 12]
    System.out.printf("Cipher bytes: " + Arrays.toString(cipherBytes) + "%n");

    //FIXME somehow the cipher is shortening the message or something
    return cipherBytes;
}
Community
  • 1
  • 1
  • You may want to have a look at [this answer](http://stackoverflow.com/a/15712409/589259) of mine regarding crypto related exceptions. – Maarten Bodewes Apr 15 '16 at 00:09

1 Answers1

3

You should not ignore the return value of cipher.update(messageBytes);. Furthermore, GCM uses CTR mode underneath. CTR mode doesn't require padding, so you should be using "AES/GCM/NoPadding".

You're probably just getting the last part of ciphertext and the authentication tag back at the moment (i.e. the tail rather than the head).

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • so should I funnel that return value into the cipher.doFinal() method? Alternatively I could skip that line and just call cipher.doFinal(messageBytes); I think that works the same way. – SupremeMitchell Apr 15 '16 at 19:22