从 Java 生成的数据中解密 PHP 中的 AES-128

Posted

技术标签:

【中文标题】从 Java 生成的数据中解密 PHP 中的 AES-128【英文标题】:Decrypting AES-128 in PHP from data generated in Java 【发布时间】:2017-02-12 20:35:32 【问题描述】:

使用 *** 和其他网站上的许多不同示例,我设法创建了一个有效的 Java 类,用于使用 AES-128 加密和解密字符串。我还需要在 php 中解密和加密这些相同的字符串以允许双向通信,但是使用 PHP 的 mcrypt 我得到了与我想要实现的目标不接近的乱码和损坏的字符串。

这是 Java 源代码(封装在一个函数中):

        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] keyBytes = password.getBytes("UTF-8");
        digest.update(keyBytes);
        keyBytes = Arrays.copyOf(digest.digest(), 16);

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        byte[] iv = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,;
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));

        return new String(decryptedBytes);

例如,这将使用密钥/密码J@RMZyv7~Dyd 返回dyHuhcYI3JIQ4BssSyJ3bjE/sQCOH+fWq2EujW579BU=,并且可以使用相同的密钥解密为Dit bericht is versleuteld!

在 PHP 中,我使用的代码要少得多,而且我不确定哪里出了问题。我使用V1gbTVCCbQK7cknbbir5Gg==作为解密密钥,也就是上面keyBytes变量的base64编码版本。

$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, base64_decode("V1gbTVCCbQK7cknbbir5Gg=="), base64_decode("vdyHuhcYI3JIQ4BssSyJ3bjE/sQCOH+fWq2EujW579BU="), MCRYPT_MODE_CBC, "0000000000000000");
var_dump($decrypted);

这会产生一些损坏的字符串,我还不确定如何解决这个问题。我确信我的 PHP 实现有问题,因为我可以在 Java 中成功地对其进行加密和解密。

【问题讨论】:

加密的诀窍是你不想让它工作,你希望它是安全的。上述代码或您答案中的代码不安全;它不使用正确的密钥推导,不应使用零 IV。它还缺少对完整性和真实性的保护,并且 - 当用于点对点通信时 - 它也不会是机密的。 最好不要使用mcrypt,它是废弃软件,多年未更新,不支持标准PKCS#7(née PKCS#5)填充,只有非标准空填充可以'甚至不能与二进制数据一起使用。 mcrypt 有许多出色的 bugs 可以追溯到 2003 年。请考虑使用 defuse 或 RNCryptor,它们提供了完整的解决方案并且正在维护并且是正确的。 @zaph 好话。我会研究那些库,我确实不应该使用它。 @Maarten 你是对的。我仍在尝试这个,因为我没有为我的用例找到任何具体的例子,但我将看看各种开源项目如何处理我想要实现的同一件事。 【参考方案1】:

事实证明,我使用的 IV 不太正确。在尝试了this答案中的问题后,我发现使用以下代码可以成功解密我的消息:

function hexToStr($hex)

    $string = '';
    for ($i = 0; $i < strlen($hex) - 1; $i += 2) 
        $string .= chr(hexdec($hex[$i] . $hex[$i + 1]));
    
    return $string;

$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, base64_decode($key), base64_decode($encrypted), MCRYPT_MODE_CBC, hexToStr('00000000000000000000000000000000'));

请注意,IV 现在使用的是该函数,而不是值“0000000000000000”。

【讨论】:

您的代码仍然不安全。这不打扰你吗? 这确实让我很困扰@luke。我仍在考虑如何在使用套接字时使 Java 和 PHP 之间的通信尽可能安全和简单。对于最终用户来说,它也需要简单;生成证书并不是那么简单。

以上是关于从 Java 生成的数据中解密 PHP 中的 AES-128的主要内容,如果未能解决你的问题,请参考以下文章

Java & PHP & Javascript 通用 RSA 加密 解密 (长字符串)

rsa互通密钥对生成及互通加解密(c#,java,php)

Java RSA解密

解密随机数生成器——从java源码看线性同余算法(转)

java进行3des加密传过来的数据,php怎么解密?

Java 中的 AES 128 加密 PHP 中的解密