【问题标题】:RIJNDAEL encryption with Java使用 Java 进行 RIJNDAEL 加密
【发布时间】:2012-04-13 07:05:12
【问题描述】:

我需要在 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

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

}

输出为(与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 版本之间的区别是什么吗?

【问题讨论】:

  • 更改行 System.out.println(new String(encrypted)); 时加密的输出是否相同;
  • 谢谢,我的错。我修改了我的帖子,但输出仍然不一样
  • 在我的答案的编辑中添加了一件需要的东西

标签: java php rijndael


【解决方案1】:

没有很好的记录,但是 PHP 的 MCRYPT_RIJNDAEL_128MCRYPT_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);

【讨论】:

  • 你可以做 String signature = Base64.encodeToString(output)
  • 对类似的problem 有任何帮助吗? @Raider
【解决方案2】:

这里有三件事:

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

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

  • PHP 版本没有填充,而 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");
...

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

【讨论】:

  • 谢谢,我将模式更改为 CFB 并调整了我的帖子。不幸的是,输出仍然不一样。我将查看您提到的 AES Wiki 条目链接...
  • 我错过了一个更重要的事情:java 版本使用 PKCS5Padding,而 PHP 版本没有。我将添加一个编辑
  • 再次感谢,我调整了我的帖子,但它仍然不一样......无论如何,只有在我的情况下输入字符串的长度不是 16 的乘积时,填充才相关,不是是吗?
  • 井填充可能更复杂,但在这种情况下似乎并非如此。但问题是: generateKey() 是做什么的?您从不使用 String key = "..." 的值,这似乎是个问题?
  • 对不起,我是用来测试的,不小心复制了。帖子已经改编...
猜你喜欢
  • 2020-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多