RIJNDAEL encryption with Java
Asked Answered
C

2

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?

Cullan answered 13/4, 2012 at 7:5 Comment(3)
Is the output same for encrypted when you change the line System.out.println(new String(encrypted));Thy
Thanks, my bad. I adapted my post but the output is still not the sameCullan
one more thing added in the edit of my answer that will be neededFragmental
I
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);
Iodate answered 22/5, 2012 at 16:3 Comment(2)
you could do String signature = Base64.encodeToString(output)Apply
any help on a similar problem? @IodateApply
F
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.

Fragmental answered 13/4, 2012 at 7:12 Comment(9)
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...Cullan
one more important thing I missed: the java version uses PKCS5Padding, while the PHP version has none. I'll add in an editFragmental
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?Cullan
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?Fragmental
Sorry, I used this for testing and copied it by accident. The post is already adapted...Cullan
perhaps give the edit a try? Java crypto is so clumsy, and I don't know a ton about PHP cryptoFragmental
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 JavaCullan
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???Cullan
@mfrankli any help on a similar problem?Apply

© 2022 - 2024 — McMap. All rights reserved.