fret
fret

Reputation: 113

Data differs after encryption and consecutive decryption with AES

I want to encrypt and decrypt integers with AES but can't get it going.

To test the basic cryptographic process I wrote a simple method that takes input data, encrypts and decrypts it with the same parameters and returns the result.

Here is my failing JUnit test case that checks whether the input and the output data are equal.

    @Test
public void test4() throws UnsupportedEncodingException {

    Random random = new Random();

    SecretKey secretKey = Tools.generateKey("secretKey".getBytes("UTF-8"));

    byte[] initializationVector = Tools.intToByteArray(random.nextInt()); 
    // ensuring that the initialization vector has the correct length
    byte[] ivHash = Tools.hashMD5(initializationVector);

    int value = random.nextInt();
    byte[] input = Tools.intToByteArray(value);

    byte[] received = Tools.enDeCrypt(input, secretKey, ivHash);        
    assertEquals(data.hashCode(), received.hashCode());
}

Method generateKey:

public static SecretKeySpec generateKey(byte[] secretKey) {

    try {
        // 256 bit key length
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(secretKey);
        byte[] key = md.digest();
        return new SecretKeySpec(key, "AES");
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }
}

Method for int -> byte[] conversion:

public static byte[] intToByteArray(int a) {
    // block size is 128 bit, thus I append zeros 

    byte[] intByte = ByteBuffer.allocate(4).putInt(a).array();
    byte[] longerIntByte = new byte[16];
    for (int i = 0; i < 4; i++) {
        longerIntByte[i] = intByte[i];
    }
    for (int i = 4; i < longerIntByte.length; i++) {
        longerIntByte[i] = 0;
    }
    return longerIntByte;
}

Here is the code for encryption and decryption:

public static byte[] enDeCrypt(byte[] data, SecretKey secretKey,
        byte[] initialisationVector) {

    try {
        IvParameterSpec ivSpec = new IvParameterSpec(initialisationVector);
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");

        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
        byte[] encrypted = cipher.doFinal(data);

        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
        byte[] decrypted = cipher.doFinal(encrypted);
        return decrypted;

    } catch (NoSuchAlgorithmException | NoSuchPaddingException
            | InvalidKeyException | InvalidAlgorithmParameterException
            | IllegalBlockSizeException | BadPaddingException e) {
        throw new RuntimeException(e);
    }

}

Upvotes: 0

Views: 190

Answers (1)

ntoskrnl
ntoskrnl

Reputation: 5744

assertEquals(data.hashCode(), received.hashCode()) is very unlikely to pass unless data and received refer to the same object (since byte arrays inherit the identity hash code method from Object). I don't see where data comes from, but that is probably not the case here. You should use Arrays.equals(data, received).

Apart from that, there are various cryptographic issues here:

  • Random is not "random enough" for cryptographic purposes; you should use SecureRandom.
  • Key derivation using plain SHA-256 is dubious. You should consider using a key derivation algorithm that is specifically designed for this, like PBKDF2.
  • AES with 256-bit keys is not always better than 128-bit keys. Check this page. In this case it's completely bogus since passphrases rarely even reach 128 bits of entropy.
  • Random IVs – good, but why jump through hoops when you could just directly use SecureRandom.nextBytes(). Hashing the IV doesn't add anything useful.
  • There's no reason to do manual zero padding when you could instead let the library handle it. Just specify PKCS5Padding instead of NoPadding.

Upvotes: 4

Related Questions