5

I need to encode a cleartext in Java and php where the result must be the same.

The following conditions are given:

  1. algorithm: RIJNDAEL-128
  2. key: 1234567890123456
  3. mode: cfb
  4. initialization vector: 1234567890123456

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?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Paul
  • 547
  • 4
  • 13
  • 30

2 Answers2

8

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);
Raider
  • 740
  • 6
  • 11
  • you could do String signature = Base64.encodeToString(output) – olleh Jan 13 '15 at 08:19
  • any help on a similar [problem](http://stackoverflow.com/questions/27921093/rewrite-php-rijndael-algorithm-to-java-android)? @Raider – olleh Jan 14 '15 at 02:42
3

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.

mfsiega
  • 2,852
  • 19
  • 22
  • Thanks, I changed the mode to CFB and adapted my post. Output is unfortunatelly still not the same. I will take a look in the AES Wiki entry link that you mentionend... – Paul Apr 13 '12 at 07:41
  • one more important thing I missed: the java version uses PKCS5Padding, while the PHP version has none. I'll add in an edit – mfsiega Apr 13 '12 at 07:44
  • Thanks again, I adapted my post, but it is still not the same... Anyway, Padding is only relevant if length of the input string is not a multiply of 16 in my case, isn't it? – Paul Apr 13 '12 at 08:05
  • well padding can be more complicated, but it doesn't seem to be in this case. question though: what does generateKey() do? you're never using the value of String key = "...", which seems like it could be a problem? – mfsiega Apr 13 '12 at 08:14
  • Sorry, I used this for testing and copied it by accident. The post is already adapted... – Paul Apr 13 '12 at 08:44
  • perhaps give the edit a try? Java crypto is so clumsy, and I don't know a ton about PHP crypto – mfsiega Apr 13 '12 at 09:01
  • Thanks, but problem is, that the key is given. My actual task looks like this. Take the php code (and I don't know lot about php) and translate it into Java – Paul Apr 13 '12 at 09:38
  • Hi, I neraby oversaw this part of your answer: "Essentially, the String key is a seed for generating a SecretKey, rather than the key itself." But how can I get the key then??? – Paul Apr 16 '12 at 09:18
  • @mfrankli any help on a similar [problem](http://stackoverflow.com/questions/27921093/rewrite-php-rijndael-algorithm-to-java-android)? – olleh Jan 14 '15 at 02:43