首页 > 技术分享 > 原生PHP
收藏

PHP实现AES级别加密解密

07/07 10:40
大潇博客 原创文章,转载请标明出处

文章中很多内容来自同行见解,本人亲自试验所得结果


在对接一些需要验证且保密性较强的系统时,可能会用到AES及以上级别的加密,AES是对称性加密里面常见一种,是一种使用密钥加密的块算法


跨语言做AES加密解密经常会出现问题,往往是填充方式不对、编码不一致或者加密解密模式没有对应上造成


经过几天对PHP的AES加密解密反复测试,仿佛PHP又有种独特的解决方法,当然不是说它有多特殊或是和别的语言有很大差异,只是在很多语言中加密的数据,发现PHP中的openssl_decrypt竟然解不开密


这包括js、ios等,AES加密后通常有两种处理方法,使用base64_encode密文,或者使用bin2hex转成16进制,问题最大的是转成16进制,PHP加密后的AES密文字符串,有些语言是可以解开的,但其它语言加密后的16进制字符串,尝试了PHP的各种方式暂时都不可解,这可能就是上文提及的:填充方式不对、编码不一致或者加密解密模式没有对应上造成


在PHP7.1之前,PHP的AES级别加密,可以通过mcrypt扩展库实现,查阅相关资料了解,mcrypt_系列函数对跨语言加密解密,兼容性较好,但从PHP7.1开始,PHP不建议使用此扩展进行加解密,并在7.2版本彻底废弃,其实早在2015年就已经开始建议大家使用openssl_encrypt/openssl_decrypt来代替mcrypt_encrypt/mcrypt_decrypt,缓冲了很久,这一天终于在7.2.0版本上到来了,所以为了我们的代码能够更长久,建议使用openssl_encrypt/openssl_decrypt进行加密解密


多说一句:mcrypt扩展的主要问题是,它是基于libmcrypt的,自2007年开始就没有开发过。因此,即使libmcrypt还在使用,但是由于缺乏适当的开发和维护,库的安全问题已经成为许多系统管理员关注的焦点。

由于mcrypt扩展开发的结束,扩展也从PHP 7.2中删除,并转移到一个非官方的PECL存储库中。但是,你仍然可以在PHP 5.4到PHP 7.1中找到mcrypt扩展。PHP 7.2已经发布,但是它不包含mcrypt扩展。对于PHP 7.2+, PHP使用libsodium作为加密库。


此处又不得不提上文说到的,openssl_encrypt/openssl_decrypt对跨语言加密解密不太友好,可能需要反复调试,在加密前后加一些辅助验证和过滤代码,最终匹配到适合自己的方案


下面说一下openssl_encrypt函数

openssl_encrypt($data, $method, $key, $options, $iv)

$data 加密明文

$method 加密方法,举几个列子:AES-128-ECB、AES-192-ECB、AES-256-ECB、AES-128-CBC等

$key 加密密钥[密码]

$options 数据格式填充选项(可选)选项有:

OPENSSL_RAW_DATA=1

OPENSSL_ZERO_PADDING=2

OPENSSL_NO_PADDING=3

$iv 密初始化偏移量/向量(可选)

其中

$method 加密方法中的128、192、256,分别表示AES的有效块大小为128、192或256位(16、24或32字节)

$iv偏移量,只有在ECB模式中不用,其它必须使用,长度为16字节,不使用此项会抛出一个警告,如果未进行手动填充, 则返回加密失败

关于$options填充选项说明:

0 : 自动对明文进行 padding, 返回的数据经过 base64 编码.(默认PKCS7填充方式)

1 : OPENSSL_RAW_DATA, 自动对明文进行 padding, 但返回的结果未经过 base64 编码.(默认PKCS7填充方式)

2 : OPENSSL_ZERO_PADDING, 自动对明文进行 0 填充, 返回的结果经过 base64 编码. 但是, openssl 不推荐 0 填充的方式, 即使选择此项也不会自动进行 padding, 仍需手动 padding.

3 : OPENSSL_NO_PADDING,不进行填充



很多时候解密失败,openssl_decrypt返回false,归根结底是openssl_decrypt解密方式不对,尝试更换options参数,测试中很多情况使用OPENSSL_ZERO_PADDING或者是2,就能解开


尽量使用base64进行最后的转码,测试中PHP对此方式兼容性较好

若再次遇到解密失败的情况,不妨记录观察一下加密后的字符串,是否包含“+”,在http传输中,参数中直接带加号往往会被过滤成空格,如果确实有加号,加密后的字符串使用urlencode编码后再传参即可


在测试中,PHP使用OPENSSL_RAW_DATA进行填充得到的加密字符串,使用OPENSSL_RAW_DATA进行解密,一切可以正常实行,但加密后的字符串在其它语言中解密,或在PHP中使用OPENSSL_ZERO_PADDING解密,解密后的字符串末尾,会自动添加一些空格,ios中显示为“\0”,可以在当前语言中过滤


实际使用中OPENSSL_ZERO_PADDING填充是兼容性最好的,不过往往在字符串结尾,会显示多出一些空字符串,但使用trim或者str_replace进行空字符串过滤却又无效,这就说明并不是真正的空字符串,我们可将其转为ascii后删除32(含)以下的字符

$sArray = str_split($pwd);

foreach ($sArray as $item) {

$assii = ord($item);

if ($assii <= 32){

      $pwd = str_replace(chr($assii),"", $pwd); //删除此ASCII字符

}

}


打赏

阅读排行

大家都在搜

博客维护不易,感谢你的肯定
扫码打赏,建议金额1-10元
  • 15601023311