用Java加密RIJNDAEL

Pau*_*aul 4 php java rijndael

我需要在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));       
        }
    ?>
Run Code Online (Sandbox Code Playgroud)

输出为: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();
    }

}
Run Code Online (Sandbox Code Playgroud)

输出是(与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));       
        }
    ?>
Run Code Online (Sandbox Code Playgroud)

这导致以下输出: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();
    }

}
Run Code Online (Sandbox Code Playgroud)

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

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

Rai*_*der 7

它不记录良好,但PHP的MCRYPT_RIJNDAEL_128使用MCRYPT_MODE_CFB产生的结果与Java的一致AES/CFB8/NoPadding.

所以在PHP中这一行:

$encrypted = base64_encode( mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $cleartext, MCRYPT_MODE_CFB, $iv ) );
Run Code Online (Sandbox Code Playgroud)

在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);
Run Code Online (Sandbox Code Playgroud)


mfr*_*kli 3

这里有三件事:

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

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

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

此外,您实际上需要生成一个 AES 密钥,而不是使用密钥字符串的字节构造 SecretKeySpec。这看起来像:

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");
...
Run Code Online (Sandbox Code Playgroud)

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