Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RIJNDAEL encryption with Java

Tags:

java

php

rijndael

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?

like image 725
Paul Avatar asked Apr 13 '12 07:04

Paul


2 Answers

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);
like image 129
Raider Avatar answered Sep 20 '22 09:09

Raider


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.

like image 43
mfsiega Avatar answered Sep 19 '22 09:09

mfsiega