【问题标题】:PHP Java AES CBC Encryption Different ResultsPHP Java AES CBC 加密不同的结果
【发布时间】:2012-06-06 05:40:04
【问题描述】:

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 中获得相同的结果吗?非常感谢。

【问题讨论】:

    标签: java php encryption aes


    【解决方案1】:

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

    其实例外是:

    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 的手册页。它说:

    将使用给定密码和模式加密的数据。如果数据的大小不是n * blocksize,数据会被\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 编码/解码,而不是使用未记录的内部方法。

    【讨论】:

    • 非常感谢,量子力学。解释得很好。
    • 请注意,通常不使用基于零的填充,因为您无法区分数据末尾的零值字节和填充。您应该改用 PKCS#7 填充(Java 中的"PKCS5Padding")。另请注意,使用 sun.* 功能不符合所有 Java 兼容性准则。
    猜你喜欢
    • 1970-01-01
    • 2022-11-16
    • 2018-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多