I am trying to decrypt an AES cipher in Java. The cipher was encrypted/decrypted in PHP using the openssl_encrypt/openssl_decrypt function.
An example of decryption in PHP looks like this:
function decryptSerial($encrypted_txt){
$encrypt_method = 'AES-256-CBC';
$key = hash('sha256', $secret_key);
//iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
$iv = substr(hash('sha256', $secret_iv), 0, 16);
return openssl_decrypt(base64_decode($encrypted_txt), $encrypt_method, $key, 0, $iv);
}
echo decryptSerial('bnY0UEc2NFcySHgwRTIyNFU1NU5pUT09'); //output is MXeaSFSUj4az
The PHP code uses AES-256-CBC with no padding to decrypt so I do the same in Java:
public static String decryptAES256CBC(String cipherText, String keyString, String ivString){
try {
// Truncate the key at the first 32 bytes
byte [] keyBytes = keyString.substring(0,32).getBytes();
byte [] ivBytes = ivString.getBytes();
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec iv = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("AES_256/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, iv);
byte [] decodedCipher = java.util.Base64.getDecoder().decode(cipherText);
byte[] plainText = cipher.doFinal(decodedCipher);
return java.util.Base64.getEncoder().encodeToString(plainText);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException |
InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
throw new RuntimeException(e);
}
}
public static void main(String [] args){
String key = generateSHA256("*****");
String iv = generateSHA256("******").substring(0,16);
System.out.println(decryptAES256CBC("bnY0UEc2NFcySHgwRTIyNFU1NU5pUT09", key, iv));
}
This however does not work. When I run it with the example input, I get the error mentioned in the title. It seems like by input cipher is not of the correct length - which is true, when I base64 decode the cipher I get a byte array of length 24 a.k.a not a multiple of 16. This would require padding to get things to work I believe. But then how does the PHP code do it without padding?
I tried recreating the PHP code in Kava. I researched the openssl_decrypt function and ported its functionality. However, when I ran it in Java, it seems like I need padding. The PHP code used no padding if I am not mistaken.
In the PHP code, key and IV are derived using SHA256, hex encoded, and these values are applied UTF-8 encoded as key and IV. The key is implicitly truncated to 32 bytes (by
openssl_decrypt()) and the IV explicitly to 16 bytes. In Java, both truncations must be performed explicitly, e.g.:Remark: The PHP code and the
generateSHA256()implementation above apply lowercase hex digits. For older Java versions that did not supportHexFormat, care must be taken that the hex encoding also applies lowercase hex digits (this is in particular true for the hex encoding variants from here).Furthermore, in the PHP code the ciphertext is Base64 decoded twice: implicit by default and explicit. As padding PKCS#7 is applied by default. For a detailed description of the default values related to Base64 and padding, see the options parameter and constants.
A possible Java implementation is as follows:
Test:
In accordance with the result of the PHP code.
The PHP code has some vulnerabilities and inefficiencies (even if you can't change this, it should be noted for future readers):
Also, cross-platform compatibility issues can occur (case sensitivity of the hex digits).