I need to encode a cleartext in Java and php where the result must be the same.
The following conditions are given:
The following codes works and fulfils the first an the second requirement but it uses ECB as mode and therefore does not use an initalization vector:
PHP:
<?php
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
$cleartext = 'abcdefghijklmnop';
$key128 = '1234567890123456';
$iv = '1234567890123456';
if (mcrypt_generic_init($cipher, $key128, $iv) != -1) //Parameter iv will be ignored in ECB mode
{
$cipherText = mcrypt_generic($cipher,$cleartext );
mcrypt_generic_deinit($cipher);
printf(bin2hex($cipherText));
}
?>
Output is: fcad715bd73b5cb0488f840f3bad7889
JAVA:
public class AES {
public static void main(String[] args) throws Exception {
String cleartext = "abcdefghijklmnop";
String key = "1234567890123456";
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(cleartext.getBytes());
System.out.println(asHex(encrypted));
}
public static String asHex(byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
}
Output is (the same as in the PHP version): fcad715bd73b5cb0488f840f3bad7889
So now in order to fulfill requirement 3 and 4 I changed the mode to MCRYPT_MODE_CFB in my PHP version so that the code looks like this:
<?php
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CFB, '');
$cleartext = 'abcdefghijklmnop';
$key128 = '1234567890123456';
$iv = '1234567890123456';
if (mcrypt_generic_init($cipher, $key128, $iv) != -1) //Parameter iv will be ignored in ECB mode
{
$cipherText = mcrypt_generic($cipher,$cleartext );
mcrypt_generic_deinit($cipher);
printf(bin2hex($cipherText));
}
?>
This results in the following output: 14a53328feee801b3ee67b2fd627fea0
In the JAVA version I also adapted the mode and added the iv to the init function of my Cipher object.
public class AES {
public static void main(String[] args) throws Exception {
String cleartext = "abcdefghijklmnop";
String key = "1234567890123456";
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec("1234567890123456".getBytes()));
byte[] encrypted = cipher.doFinal(cleartext.getBytes());
System.out.println(asHex(encrypted));
}
public static String asHex(byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
}
But here the output is 141eae68b93af782b284879a55b36f70 which is different to the PHP version.
Does anybody have a clue what the difference betwenn the JAVA and the PHP version could be?
It isn't documented well, but PHP's MCRYPT_RIJNDAEL_128
with MCRYPT_MODE_CFB
produces results consistent with Java's AES/CFB8/NoPadding
.
So this line in PHP:
$encrypted = base64_encode( mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $cleartext, MCRYPT_MODE_CFB, $iv ) );
Matches up to this block in Java:
SecretKeySpec key = new SecretKeySpec(KEY.getBytes(), "AES");
IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] output = cipher.doFinal(cleartext.getBytes());
String signature = Base64.encode(output);
Three things here:
It's very possible that PHP's "MCRYPT_RIJNDAEL_128" isn't exactly the same algorithm as Java's "AES". The AES Wiki entry talks about the difference between RIJNDAEL and AES at the bottom of the intro.
You're using CBC in the PHP version, while you're using CFB in the Java version. Even if the algorithms are the same, this will definitely give you different output.
The PHP version has no padding, while the Java version is using PKCS5Padding. The Java version should instantiate cipher with "Cipher.getInstance("AES/CFB/NoPadding");"
Also, instead of constructing the SecretKeySpec with the bytes of the key String, you're going to want to actually want to generate an AES key. This will look like:
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom sec = new SecureRandom(key.getBytes());
keygen.init(128, sec);
Key key = keygen.generateKey();
SecretKeySpec skeySpec = new SecretKeySpec(key.getEncoded(), "AES");
...
Essentially, the String key is a seed for generating a SecretKey, rather than the key itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With