5

我需要在 Java 和 php 中编码一个明文,结果必须相同。

给出以下条件:

  1. 算法:RIJNDAEL-128
  2. 键:1234567890123456
  3. 模式:cfb
  4. 初始化向量:1234567890123456

以下代码有效并满足第一个和第二个要求,但它使用 ECB 作为模式,因此不使用初始化向量:

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));       
        }
    ?>

输出为:fcad715bd73b5cb0488f840f3bad7889

爪哇:

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();
    }

}

输出为(与 PHP 版本相同):fcad715bd73b5cb0488f840f3bad7889

所以现在为了满足要求 3 和 4,我在我的 PHP 版本中将模式更改为 MCRYPT_MODE_CFB,以便代码如下所示:

 <?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));       
        }
    ?>

这导致以下输出:14a53328feee801b3ee67b2fd627fea0

在 JAVA 版本中,我还调整了模式并将 iv 添加到我的 Cipher 对象的 init 函数中。

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();
    }

}

但这里的输出是 141eae68b93af782b284879a55b36f70,这与 PHP 版本不同。

有人知道 JAVA 和 PHP 版本之间的区别是什么吗?

4

2 回答 2

8

它没有很好地记录,但是 PHP 的MCRYPT_RIJNDAEL_128withMCRYPT_MODE_CFB产生的结果与 Java 的AES/CFB8/NoPadding.

所以PHP中的这一行:

$encrypted = base64_encode( mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $cleartext, MCRYPT_MODE_CFB, $iv ) );

与 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);
于 2012-05-22T16:03:33.427 回答
3

这里有三件事:

  • PHP 的“MCRYPT_RIJNDAEL_128”很可能与 Java 的“AES”不完全相同。AES Wiki 条目在介绍的底部讨论了 RIJNDAEL 和 AES 之间的区别。

  • 您在 PHP 版本中使用 CBC,而在 Java 版本中使用 CFB。即使算法相同,这肯定会给你不同的输出。

  • PHP 版本没有填充,而 Java 版本使用 PKCS5Padding。Java 版本应使用“Cipher.getInstance("AES/CFB/NoPadding");" 实例化密码。

此外,不是用密钥字符串的字节构造 SecretKeySpec,而是真正想要生成一个 AES 密钥。这看起来像:

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");
...

本质上,String 密钥是生成 SecretKey 的种子,而不是密钥本身。

于 2012-04-13T07:12:18.877 回答