java - PHP Java AES CBC加密不同结果

标签 java php encryption aes

PHP 函数:

$privateKey = "1234567812345678";
$iv = "1234567812345678";
$data = "Test string";

$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $privateKey, $data, MCRYPT_MODE_CBC, $iv);

echo(base64_encode($encrypted));

Result: iz1qFlQJfs6Ycp+gcc2z4w==

Java 函数

public static String encrypt() throws Exception{
try{
    String data = "Test string";
    String key = "1234567812345678";
    String iv = "1234567812345678";

    javax.crypto.spec.SecretKeySpec keyspec = new javax.crypto.spec.SecretKeySpec(key.getBytes(), "AES");
    javax.crypto.spec.IvParameterSpec ivspec = new javax.crypto.spec.IvParameterSpec(iv.getBytes());

    javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/NoPadding");
    cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, keyspec, ivspec);
    byte[] encrypted = cipher.doFinal(data.getBytes());

    return new sun.misc.BASE64Encoder().encode(encrypted);

}catch(Exception e){
    return null;
}

返回空值。

请注意,我们不允许更改 PHP 代码。有人可以帮助我们在 Java 中获得相同的结果吗?非常感谢。

最佳答案

如果您不简单地在 encrypt() 例程中吞下可能的 Exception,您就会更好地了解发生了什么。如果您的函数返回 null,那么显然发生了异常,您需要知道它是什么。

其实异常(exception)是:

javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
    at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:854)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:828)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313)
    at javax.crypto.Cipher.doFinal(Cipher.java:2087)
    at Encryption.encrypt(Encryption.java:20)
    at Encryption.main(Encryption.java:6)

果然,您的明文只有 11 个 Java 字符长,在您的默认编码中,将是 11 个字节。

您需要检查 PHP mcrypt_encrypt 函数的实际作用。由于它有效,它显然使用了一些填充方案。您需要找出它是哪一个并在您的 Java 代码中使用它。

好的——我查阅了mcrypt_encrypt 的手册页。它说:

The data that will be encrypted with the given cipher and mode. If the size of the data is not n * blocksize, the data will be padded with \0.

因此您需要在 Java 中复制它。这是一种方法:

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Encryption
{
    public static void main(String args[]) throws Exception {
        System.out.println(encrypt());
    }

    public static String encrypt() throws Exception {
        try {
            String data = "Test string";
            String key = "1234567812345678";
            String iv = "1234567812345678";

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            int blockSize = cipher.getBlockSize();

            // We need to pad with zeros to a multiple of the cipher block size,
            // so first figure out what the size of the plaintext needs to be.
            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;
            int remainder = plaintextLength % blockSize;
            if (remainder != 0) {
                plaintextLength += (blockSize - remainder);
            }

            // In java, primitive arrays of integer types have all elements
            // initialized to zero, so no need to explicitly zero any part of
            // the array.
            byte[] plaintext = new byte[plaintextLength];

            // Copy our actual data into the beginning of the array.  The
            // rest of the array is implicitly zero-filled, as desired.
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);

            return new sun.misc.BASE64Encoder().encode(encrypted);

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

当我运行时,我得到:

iz1qFlQJfs6Ycp+gcc2z4w==

这是你的 PHP 程序得到的。


更新(2016 年 6 月 12 日): 从 Java 8 开始,JavaSE 终于附带了一个文档化的 base64 编解码器。所以不是

return new sun.misc.BASE64Encoder().encode(encrypted);

你应该做类似的事情

return Base64.Encoder.encodeToString(encrypted);

或者,使用第 3 方库(例如 commons-codec)进行 base64 编码/解码,而不是使用未记录的内部方法。

关于java - PHP Java AES CBC加密不同结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10842509/

相关文章:

java - 在java中使用SFTP将目录复制到远程计算机

php - 显示类别和子类别的查询

ruby-on-rails - Rails 在 database.yml 中为 SQL-Server 加密数据库密码

c# - 将字符串加密两次...好、坏还是丑?

java - 每次单击时创建 JLabel 的新实例

java - 什么 Web FrameWork 对 Java 开发人员有好处?

Java 正则表达式问题 - 无法匹配同一行中的两个字符串

php - Laravel Route 模型与关系绑定(bind)

php - 希望将 phpmailer 库添加到 Zend Framework 设置中

java - Spring MVC : Using User password to create a Java keystore, 和稍后的访问 key