0
votes

Have tried several different methods found on Google and SOF, but I just can't seem to get this to work. I am trying to encrypt a string in PHP and decrypt it in Java (Android activity). In PHP to encrypt the string, I'm using AES-256-CBC with a sha256 hash (which encrypts/decrypts successfully in PHP). The issue is that no matter what I try, I can't get the unencrypted string in Java. My latest attempt ended with a console error of "Cipher functions:OPENSSL_internal:WRONG_FINAL_BLOCK_Length".

I suspect my issue is with Base64 encoding, but all of my attempts so far have failed spectacularly. Or perhaps I'm using the wrong Cipher on the Java end. I've read about some people needing to convert to hex, though I didn't understand why.

Please note, the iv and key are not really the same, just using the same here for example's sake.

My PHP code:

$secret_key = "1111111111111111";
$secret_iv = "1111111111111111";

$encrypt_method = "AES-256-CBC"; 
$key = hash( 'sha256', $secret_key );
$iv = substr( hash( 'sha256', $secret_iv ), 0, 16 );
$output = base64_encode( openssl_encrypt( $string, $encrypt_method, $key, 0, $iv ) );

My Java code:

    String given_iv = "1111111111111111";
    String key = "1111111111111111";

    IvParameterSpec iv = new IvParameterSpec(given_iv.getBytes());
    SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

    byte[] decodedEncryptedData = Base64.decode(data.getBytes(),Base64.NO_WRAP);

    byte[] original = cipher.doFinal(decodedEncryptedData);
    Log.i(TAG, "Result: " + new String(original));
    return new String(original);

Can someone please tell me where I went wrong. Thanks.

Update 1: As pointed out by @Peter and @Topaco, I was Base64 encoding twice on the PHP side, this was removed as well as using the key/IV directly on the Java side. Unfortunately, the error persists.

PHP code now:

    $secret_key = "1111111111111111";
    $secret_iv = "1111111111111111";

    $encrypt_method = "AES-256-CBC"; 
    $output = openssl_encrypt( $string, $encrypt_method, $secret_key, 0, $secret_iv );

Java code still remains the same. However I was messing around with Cipher.getInstance("AES/CBC/PKCS5PADDING") and changed it to "AES/CBC/NOPADDING", it changed the error, I now get an output of garbled text, but still no real luck on decrypting fully.

1
openssl_encrypt returns the base64 encoded ciphertext by default so you are double encoding. Remove the base64_encode call or set the OPENSSL_RAW_DATA option.Peter
Unrelated to the question: don't reuse IVs! Generate a new one for each encryption using the random_bytes function. And if $secret_key has low entropy (like a password), use a key derivation function such as PBKDF2, Argon2, bcrypt, scrypt, etc. to get an encryption key. sha256 isn't suitable for that.Peter
@Peter As suggested, I removed base64_encode from the php file. Now the java end gives me a different error "BadPaddingException...BAD_DECRYPT". Either way, still not decrypting successfully.steven
In addition to the double Base64-encoding issue: In the PHP code you create hash values from $secret_key / $secret_iv and use them as key / IV. In the Java code you use the values directly as key / IV (i.e you do not use the hash values). Note also, that in the PHP code the 3rd parameter of the hash method should be TRUE for a binary result (otherwise you get the result as hex string, which I don't think is intended).Topaco
If you are using a 16 byte key, of course you must also use AES-128. In Java, this happens implicitly because the key length determines the variant, i.e. a 16 byte key implicitly means AES-128. In PHP, the variant is explicitly specified (the key is always implicitly adjusted by either truncating or extending by padding with 0 values), and the current variant is AES-256: So in the PHP code, simply change $encrypt_method from AES-256-CBC to AES-128-CBC.Topaco

1 Answers

0
votes

Was finally able to find a working solution thanks to @Topaco. As they pointed out, not only was I double encoding (Base64) but I should have been using AES-128-CBC instead of AES-256-CBC. As I found out, open_ssl in php doesn't seem to throw an error when the IV is of an invalid length, rather it pads or removes characters as necessary. AES-128-CBC worked with the 16-bit IV correctly and everything was solved.

For those who may stumble across this post in the future, the final solution was to change the PHP side as follows:

$secret_key = "1111111111111111";
$secret_iv = "1111111111111111";

$encrypt_method = "AES-128-CBC"; 
$output = openssl_encrypt( $string, $encrypt_method, $secret_key, 0, $secret_iv );