I wanted to implement a simple yet secure way to send some information from a PHP script to a Java client. I already looked through several implementations on here, but none worked so far and I feel a frustrate now. The last one I used with a bit of editing is this one:
PHP:
function enc3($plaintext) {
$length = 16;
$key = openssl_random_pseudo_bytes($length);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,
$plaintext, MCRYPT_MODE_CBC, $iv);
echo base64_encode($ciphertext);
echo "\n".base64_encode($iv);
echo "\n".base64_encode($key);
}
enc3("test");
Java:
public static byte[] decrypt(byte[] key, byte[] initVector, byte[] encryptedValue) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector);
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(encryptedValue);
return original;
} catch (Exception ex) {
Logger.getLogger(MainGUI.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
...
byte[] encpryted = Base64.getDecoder().decode(rd.readLine());
byte[] iv = Base64.getDecoder().decode(rd.readLine());
byte[] key = Base64.getDecoder().decode(rd.readLine());
byte[] output = decrypt(key, iv, encpryted);
And I get a:
javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
The key is just for debugging in there, of course, I would remove it when it's working, and store it in the client. Not sure what's the best way to do that, somehow, I thought of hiding it via combining it in a hidden way, the server too knows how to produce it. But I guess it would then be readable in memory, no?
1 Answer 1
AES is a block cipher and only works properly with blocks of 16 bytes. When your ciphertext length is not divisible by 16, it causes IllegalBlockSizeException afterwards.
You can easily implement PKCS5 padding for AES/CBC cipher in PHP, which is basically a standardized way how to pad your plaintext input to be an exact multiple of the block size:
function pkcs5_pad($text) {
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$pad = $size - (strlen($text) % $size);
return $text . str_repeat(chr($pad), $pad);
}
Then update your encryption function to use padding:
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,
pkcs5_pad($plaintext), MCRYPT_MODE_CBC, $iv);
Java has already implemented support for PKCS5, just update AES cipher initialization:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
mcrypt_encrypt()has been DEPRECATED.encryptedValue?