使用 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 一起玩得很好

AES加解密使用总结

PHP 从 MCRYPT_MODE_ECB 切换到 AES-256-ECB

无法使用来自 AES-256-CBC 的 pgcrypto 解密,但 AES-128-CBC 可以

PHP AES 256 加密奇怪的字符

AES加密算法256位密钥与128位密钥的不同是啥?