从 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的主要内容,如果未能解决你的问题,请参考以下文章