2

I am trying to write an encryption/decryption utility class, but not matter what I do I cannot seem to get decryption working. I keep getting a javax.crypto.BadPaddingException: Given final block not properly padded exception during decryption.

I've looked at a number of examples and other stack overflow questions but can't seem to find my mistake

public class EncryptionUtil {

    private static final Log LOGGER = LogFactory.getLog(EncryptionUtil.class);
    private static final String CIPHER_MODE = "AES/CBC/PKCS5PADDING";
    private static final String CRYPTO_PROPERTIES_PATH = "/crypto.properties";
    private static final SecretKeySpec sKey = keySpecFromProperties();

    private EncryptionUtil() {}

    public static byte[] encrypt(byte[] aBytes) {
         try {
            SecureRandom lSecureRandom = new SecureRandom();
            byte[] ivBytes = new byte[16];
            lSecureRandom.nextBytes(ivBytes);
            IvParameterSpec lSpec = new IvParameterSpec(ivBytes);
            Cipher cipher = Cipher.getInstance(CIPHER_MODE);
            cipher.init(Cipher.ENCRYPT_MODE, sKey, lSpec);
            byte[] encryptedBytes = cipher.doFinal(aBytes);
            byte[] outBytes = new byte[encryptedBytes.length + 16];
            System.arraycopy(ivBytes, 0, outBytes, 0, 16);
            System.arraycopy(encryptedBytes, 0, outBytes, 16, encryptedBytes.length);

            return outBytes;
        } catch (Exception aEx) {
            LOGGER.error("Failed to encrypt bytes");
            throw new RuntimeException(aEx);
        }
    }

    public static byte[] decrypt(byte[] aBytes) {
        try {
            byte[] lIvBytes = Arrays.copyOfRange(aBytes, aBytes.length - 16, aBytes.length);
            byte[] lEncryptedBytes = Arrays.copyOfRange(aBytes, 0, aBytes.length - 16);
            IvParameterSpec lIvSpec = new IvParameterSpec(lIvBytes);
            Cipher cipher = Cipher.getInstance(CIPHER_MODE);
            cipher.init(Cipher.DECRYPT_MODE, sKey, lIvSpec);
            return cipher.doFinal(lEncryptedBytes);
        }catch (Exception aEx){
            LOGGER.error("Failed to decrypt bytes. Returning input bytes", aEx);
            return aBytes;
        }
    }

    private static SecretKeySpec keySpecFromProperties(){
        try(InputStream lPropStream = EncryptionUtil.class.getResourceAsStream(CRYPTO_PROPERTIES_PATH)){
            Properties cryptoProps = new Properties();
            cryptoProps.load(lPropStream);
            String lSecret = cryptoProps.getProperty("secret");
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            digest.update(lSecret.getBytes("UTF-8"));
            byte[] keyBytes = new byte[16];
            System.arraycopy(digest.digest(),0, keyBytes, 0, keyBytes.length);
            return new SecretKeySpec(keyBytes, "AES");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
President James K. Polk
  • 40,516
  • 21
  • 95
  • 125
rykeeboy
  • 645
  • 2
  • 8
  • 22
  • You can't store a key in a Properties file. It's binary, not text. – user207421 Sep 22 '17 at 01:00
  • I convert the string into bytes using the same solution as in the answer here https://stackoverflow.com/questions/3451670/java-aes-and-using-my-own-key – rykeeboy Sep 22 '17 at 01:25

1 Answers1

2

You prepend your IV to the ciphertext on encryption, but on decryption you copy the last 16 bytes as your IV.

Whatever you do on encryption you must undo on decryption.

President James K. Polk
  • 40,516
  • 21
  • 95
  • 125