PHP AES 加密...不知道我在做啥

Posted

技术标签:

【中文标题】PHP AES 加密...不知道我在做啥【英文标题】:PHP AES encryption... no idea what I'm doingPHP AES 加密...不知道我在做什么 【发布时间】:2011-05-12 03:41:19 【问题描述】:

我对加密知之甚少,但我能够让 AES 在 php 中工作......有点。以下是我正在使用的几个函数:

function aes_decrypt($val,$ky) 
 
    $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 
    for($a=0;$a<strlen($ky);$a++) 
      $key[$a%16]=chr(ord($key[$a%16]) ^ ord($ky[$a])); 
    $mode = MCRYPT_MODE_ECB; 
    $enc = MCRYPT_RIJNDAEL_128; 
    $dec = @mcrypt_decrypt($enc, $key, $val, $mode, @mcrypt_create_iv( @mcrypt_get_iv_size($enc, $mode), MCRYPT_RAND) ); 
    return rtrim($dec,(( ord(substr($dec,strlen($dec)-1,1))>=0 and ord(substr($dec, strlen($dec)-1,1))<=16)? chr(ord( substr($dec,strlen($dec)-1,1))):null)); 
 

function aes_encrypt($val,$ky) 
 
    $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 
    for($a=0;$a<strlen($ky);$a++) 
      $key[$a%16]=chr(ord($key[$a%16]) ^ ord($ky[$a])); 
    $mode=MCRYPT_MODE_ECB; 
    $enc=MCRYPT_RIJNDAEL_128; 
    $val=str_pad($val, (16*(floor(strlen($val) / 16)+(strlen($val) % 16==0?2:1))), chr(16-(strlen($val) % 16))); 
    return mcrypt_encrypt($enc, $key, $val, $mode, mcrypt_create_iv( mcrypt_get_iv_size($enc, $mode), MCRYPT_RAND)); 
 

这些是从comment on the PHP documentation page for mcrypt 略微修改的。 (我从 dev_urandom 更改为 rand,因为我在 windows 盒子上,其中 dev_urandom 不可用。)

无论如何,我在这个函数中使用的键是这样定义的:

define("PSK", pack("H*", "abcd7b5ca46e12345678a8161fdacee9"));

我这样调用我的函数:

echo bin2hex(aes_encrypt("wootwootwootwootwootwootwoo", PSK));

现在,生成的十六进制字符串的前 16 个字节(32 位)没问题。接下来的 16 个字节与预期的不匹配。

看,我将这些数据发布到外部网络服务,然后解密它。我(不幸地)不能在不分发我的加密密钥和数据的情况下给出我拥有的一个测试用例。对此我感到非常抱歉,但我希望熟悉 mcrypt 的人可以看看这个并告诉我我做错了什么。

再次,对于缺乏可靠的测试用例感到抱歉,但我非常感谢您提供的任何帮助!

编辑: 我发布到的提供商似乎使用的是空 IV。按照 Rook 的建议,我已经切换到 CBC 模式,并删除了与密钥相关的不必要代码。这是我的新功能:

function aes_decrypt($val,$key)

    $mode = MCRYPT_MODE_CBC;
    $enc = MCRYPT_RIJNDAEL_128; 
    $dec = @mcrypt_decrypt($enc, $key, $val, $mode, null); 
    return rtrim($dec,(( ord(substr($dec,strlen($dec)-1,1))>=0 and ord(substr($dec, strlen($dec)-1,1))<=16)? chr(ord( substr($dec,strlen($dec)-1,1))):null)); 


function aes_encrypt($val,$key) 

    $mode = MCRYPT_MODE_CBC;
    $enc=MCRYPT_RIJNDAEL_128; 
    $val=str_pad($val, (16*(floor(strlen($val) / 16)+(strlen($val) % 16==0?2:1))), chr(16-(strlen($val) % 16))); 
    return mcrypt_encrypt($enc, $key, $val, $mode, null); 

【问题讨论】:

【参考方案1】:

此加密服务很可能使用了不同的分组密码操作模式,例如 CBC。如果空 iv 与 CBC 模式一起使用,则 ECB 和 CBC 的第一个块(在本例中为 16 个字节)将产生相同的密文。任何人都不应以任何理由使用 ECB 模式。

这是一个 ECB 模式加密消息的示例:

【讨论】:

您的 cmets 很有帮助,我很欣赏最后的编辑,尽管没有它您绝对是正确的。在对 ECB 和 CBC 的工作原理进行了一些研究之后,我转向了 CBC。但是,我无法获得可与我的在线服务一起使用的输出,而我无法控制。我设置了一个空 IV(是的,很可怕),第一个块符合需要,但第二个块仍然不匹配。我现在正在尝试其他模式,但你能给我的任何建议都会很有帮助。非常感谢您的时间和回复。 @Brad 您的 String2KeyFunction,(for 循环 XoR'ing 空值)并不常见。如果他们使用不同的 string2key,那么它可能会导致问题。虽然如果您使用的密钥小于 16 字节,那么它只是一个明文密钥,这是一种糟糕的设计,但也是一种常见的设计。很抱歉第一次发帖,那是不必要的。 @Rook,事实证明这个位并不重要,因为我传递了一个正确长度的二进制密钥。我认为这只存在于处理奇数长度的键。我已将其删除,并且仅使用预定义的密钥。我相信关键部分是正确的,因为第一个块匹配。只有第二个块没有。我将编辑我上面的帖子以反映我当前的功能。 @Brad 是的,这是一个奇怪的问题,我认为它肯定是带有 null iv 的 cbc。也许它是填充,但这在 s2k 函数中不太可能。您可以尝试其他模式,但 ecb 和 cbc 是最相似的。告诉我你的想法。 @Brad,你明白了吗?【参考方案2】:

我和我的一所大学正在编写 iPhone 应用程序并使用上述方法加密和解密数据。但是当我加密要从他的 iPhone 读取的数据时,我们发现了一个问题。 iPhone 使用 PKCS7 填充。上面的代码添加了额外的填充,这将导致 iPhone 解密方法失败。我们修改了代码以解决当前问题:

public static function  aes128Encrypt($key,$val) 

    $mode = MCRYPT_MODE_CBC;
    $enc=MCRYPT_RIJNDAEL_128; 
    $blocksize= mcrypt_get_block_size($enc,$mode);
    $stringLength = strlen($val);
    $paddingLength =$blocksize-($stringLength%$blocksize);
    $val=str_pad($val,$paddingLength+$stringLength,chr($paddingLength));
    return base64_encode(mcrypt_encrypt($enc, $key, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")); 

【讨论】:

以上是关于PHP AES 加密...不知道我在做啥的主要内容,如果未能解决你的问题,请参考以下文章

AES加密

php aes和rsa加密的区别

PHP的aes加解密算法

PHP AES 加密 PKCS5Padding

PHP对称加密-AES

AES-256-CBC用PHP加密并用Java解密