使用 Rijndael_256(AES) 从 PHP mcrypt 解密 Java 中的数据
Posted
技术标签:
【中文标题】使用 Rijndael_256(AES) 从 PHP mcrypt 解密 Java 中的数据【英文标题】:Decrypting data in Java from PHP mcrypt using Rijndael_256(AES) 【发布时间】:2014-06-25 16:59:32 【问题描述】:几天来,我一直在尝试将加密数据从 php 服务获取到我的 android 应用程序。我想使用 Rijndael 256 位加密。
在 PHP 中加密和解密字符串可以正常工作。 我所做的是我将数据从 php 发送到 android 如下结构 = (初始化向量 + hash(md5) + 加密数据)。
在 Java 中,我将接收到的字符串分成 3 个隔间。
但我在 java 中得到的是一条错误消息:IV 必须是 16 字节长。 我检查了整个互联网,试图找到解决这个问题的方法。 一些人建议不要在 php 中使用 mcrypt,而另一些人则建议使用 128 算法变体。在 PHP 中使用 128 位会破坏加密。
下面我有 2 个例子可以进一步阐明问题。
感谢您的帮助。
PHP CODE加密/解密:
function encrypt_data($data,$privk)
// Random number for feeding into AES encyption algorithm
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_RAND);
// Check if decrypted properly
$hash = md5($data);
// Encrypt the data using the privk and the iv
$encrypted = trim(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $privk, trim($data), MCRYPT_MODE_CBC, $iv));
$encode = base64_encode($encrypted);
$base64_iv = base64_encode($iv);
return $base64_iv.$hash.'='.$encode;
function decrypt_data($data,$privk)
// Split data into 3 variables: iv,hash,encdata
$arr = explode('=',$data,3);
$iv = base64_decode($arr[0]);
$hash = $arr[1];
$encdata = base64_decode($arr[2]);
// Decrypt using the 3 variables
$decrypted = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $privk, trim($encdata), MCRYPT_MODE_CBC, $iv));
// Check integrity of decrypted data
$hdec = md5($decrypted);
if ($hdec == $hash)
return $decrypted;
else
return false;
JAVA解密:
public String decryptJson(String data)
String[] split= data.trim().split("=");
byte[] iv = Base64.decode(split[0],3);
String hash = split[1];
byte[] encd = Base64.decode(split[2],0);
String skey = "secretkeyfromdatabase";
byte[] skeyb = skey.getBytes();
try
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeySpec skeyspec = new SecretKeySpec(skeyb,"AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,skeyspec,ivspec);
byte[] original = cipher.doFinal(encd);
return original.toString();
catch(Exception ex)
ex.printStackTrace();
return "ERROR! | "+ex+"IV:"+iv;
【问题讨论】:
在看到“IV 必须为 16 字节长”的错误消息后,您应该检查一下byte[] iv
的长度以及为什么它不是 16 字节。
@OlegEstekhin 我看到它的长度是 32 个字节。有没有办法让它接受 32 字节的 IV?
【参考方案1】:
嗯,您的问题之一是 JCE 不支持 AES 256 位(如果您不更改 JCE 策略文件,则最多 128 位)。要么对 PHP 使用 128 位加密,要么...
正如您在doc 中看到的那样,“如果需要更强大的算法(例如,具有 256 位密钥的 AES),则必须获取 JCE Unlimited Strength Jurisdiction Policy Files 并将其安装在 JDK/JRE 中。”
用户有责任验证此操作是否符合当地法规。
【讨论】:
嗯好的,谢谢你的澄清!我不清楚java根本不支持更强的算法。我确实找到了一个名为:Bouncycastle Rijndaelengine 的 java 库。我会试试那个 使用 BC 并不能解决您的问题,请参阅:***.com/questions/12895031/… 两件事:1. AES256 实际上并不比 AES128 强,Schneier advises the use of AES128 instead; 2. mcrypt中使用的“Rijndael_256”不是AES256,而是一种256位块大小的变种,基本上没人支持。 (@Menno) 其实可以使用Bouncy来规避,但是需要直接使用Bouncy Castle“轻量级”API。这可能对您的方案有用,也可能没用。 是的,我是使用密码学的新手。我想我应该只是恢复到 128 位 AES,这似乎是最合理的选择。非常感谢!【参考方案2】:“我看到它的长度是 32 字节。有没有办法让它接受 32 字节的 IV?”
没有。根本问题是 mcrypt 中的“Rijndael_256”是 Rijndael 变体,它使用 256 位块大小而不是所有 AES 版本中使用的 128 位块大小。除了 mcrypt 库之外,您将很难找到“Rijndeal_256”的任何实现。 (见How can I do this same encrypt/decrypt PHP function on ios with Objective-C?)
编辑:Bouncy Castle实际上通过RijndaelEngine
支持Rijndeal_256:Encryption in Android equivalent to php's MCRYPT_RIJNDAEL_256
“有些人建议不要在 php 中使用 mcrypt,而其他人则说要使用 128 算法变体。”
这两个建议都是正确的。 mcrypt 是一个可怕的库,但如果你必须的话,你可以使用“Rijndael_128”运行一些东西,它与 AES128 相同。
对您的代码的一些评论:您对逐字数据md5($data)
进行哈希处理,但对修剪后的明文trim($data)
进行加密。您使用 mcrypt 的默认零填充,但在 Java 中指定 PKCS5Padding,这将不起作用。当您使用 CBC 但在加密之后和解密之前没有身份验证时,您很可能容易受到填充 oracle 攻击。
“是的,我是使用密码学的新手。” - 替代品:
鉴于您目前的密码学专业水平,您应该完全避免实施任何密码术。而是使用高级库和协议。使用哪一个很大程度上取决于您的用例。例如,如果 PHP 代码在受信任的服务器上运行,而 Java 代码在客户端,您可以通过 SSL/TLS 连接到服务器(很可能使用 https)。 TLS 连接将向客户端验证服务器,您可以使用basic http authentcation 向服务器验证客户端。
【讨论】:
以上是关于使用 Rijndael_256(AES) 从 PHP mcrypt 解密 Java 中的数据的主要内容,如果未能解决你的问题,请参考以下文章
第二部分:如何让 Ruby AES-256-CBC 和 PHP MCRYPT_RIJNDAEL_128 一起玩得很好
PHP 从 MCRYPT_MODE_ECB 切换到 AES-256-ECB