当前位置: 首页 > 后端技术 > PHP

在PHP7.1中,用openssl代替mcrypt

时间:2023-03-29 18:08:05 PHP

在PHP7.1中,用openssl代替mcrypt在PHP开发中,使用mcrypt相关函数可以方便的进行AES加解密操作,但是在PHP7中废弃了mcrypt扩展.1,所以必须找到另一个实现。迁移手册中已经指出用openssl替换mcrypt,但是没有给出具体的例子。网上有很多例子,可以代替大部分场景,具体就不解释了。同样,在某些代码场景下单纯使用在线示例可能会导致代码替换前后的兼容性问题。下面说说具体的代码和原因。首先我们直接给出替换代码,然后从代码上分析问题。(本文分析的算法是AES-128-CBC)替换示例展示了两种mcrypt的使用方式,主要是padding不同(padding在下面解释)。在整个加解密过程中,完备度高的代码会自动实现padding并去掉padding,简单的代码会直接忽略padding,但是两种方式都可以正常工作;在实际开发中(7.1之前的版本),推荐使用Filling。请看下面的具体例子:mcryptencryptionwithoutpadding:$key='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';$iv='aaaaaaaaaaaaaaa';$data='数据串';$cipher=mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCCRY_)BC';mcrypt_generic_init($cipher,$key,$iv);$cipherText256=mcrypt_generic($cipher,$data);mcrypt_generic_deinit($密码);返回bin2hex($cipherText256);同样的函数openssl加密代码:$key='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;$iv='aaaaaaaaaaaaaaa';$data='数据串';$数据=$数据。str_repeat("\x00",16-(strlen($data)%16));//双引号可以解析asc-ii码\x00returnbin2hex(openssl_encrypt($data,"AES-256-CBC",$key,OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING,$iv));mcrypt使用填充的mcrypt加密:$data='数据串';//填充(去除填充并反转)$block=mcrypt_get_block_size(MCRYPT_RIJNDAEL_128,MCRYPT_MODE_CBC);$pad=$block-(strlen($data)%$block);如果($pad<=$block){$char=chr($pad);$data.=str_repeat($char,$pad);}$cipher=mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');mcrypt_generic_init($cipher,$key,$iv);$cipherText256=mcrypt_generic($cipher,$data);mcrypt_generic_deinit($密码);返回bin2hex($cipherText256);相同功能的openssl加密代码:$key='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';$iv='aaaaaaaaaaaaaaa';$data='数据串';返回bin2hex(openssl_encrypt($data,'AES-256-256-$key,OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING,$iv));以上例子全部运行成功,其中第一个例子(没有padding,但是在openssl中做了)和第二个例子(有padding,没有在openssl中使用padding)替换前后,输出是一样的,并且没有兼容性问题。可以根据代码不同的padding方式选择不同的replacementscheme,但是有3个细节需要说明为什么会有padding?为什么换成openssl后算法名称不一样?接下来,我们将详细分析填充和算法。Padding为什么要有padding,要从加密算法说起。因为在AES-128-CBC算法中,要加密的字符串会以16字节为单位进行分段,逐个计算,导致小于16字节的段会被填充。所以给出的例子中会有两种:一种是使用默认padding,另一种是独立填充。在用openssl替换时,如何选择paddingscheme,需要了解mcrypt和openssl的默认和自主padding。mcrypt默认填写php的源代码。可以看出默认会用\x00填充。实际上,它并没有填充\x00。从源码可以发现,首先申请了一个16位的空字符串,所以初始化的时候每个字节都是\x00,其实可以说没有padding,而是\x00,并且使用默认padding得到的加密字符串如下:所以解密时要把多余的\x00去掉。当然,你可以偷懒,不删除\x00。因为在PHP中,字符串“stringx00”和字符串“string”除了长度不同外,性能是一样的,所以看起来没有什么区别。如下代码://如果尾部包含多个`\x00`,则可以输出成功。trueif("string\x00"=="string"){//\x00可以用双引号解析echotrue;}padded\x00后的例子:(注意字符串的长度,可以看出paddedwith\x00会影响长度)mcryptautonomouspaddingpaddingalgorithm需要用下面的算法进行:add填充/***填充算法*@paramstring$source*@returnstring*/functionaddPKCS7Padding($source){$source=trim($source);$block=mcrypt_get_block_size(MCRYPT_RIJNDAEL_128,MCRYPT_MODE_CBC);$pad=$block-(strlen($source)%$block);如果($pad<=$block){$char=chr($pad);$来源。=str_repeat($char,$pad);}返回$源;添加填充后,字符串实际上看起来像这样:removepadding/***removepaddingalgorithm*@paramstring$source*@returnstring*/functionstripPKSC7Padding($source){$source=trim($source);$char=substr($source,-1);$num=ord($char);如果($num==62)返回$source;$source=substr($source,0,-$num);返回$来源;mcrypt同样有独立的padding方法,所以在第二个例子中,使用上面的padding算法后,可以直接用openssl_encrypt代替,不会有兼容性问题padding后的加密字符串如下:需要注意的是openssl_encrypt和openssl_decrypt内置了padding和paddingremoval,可以直接使用。除非需要自己实现padding,否则不需要考虑paddingopenssl自带的padding。openssl_encrypt提供了options参数支持独立的padding,但是在查阅php源码中的openssl测试用例代码后发现正确的用法://如果我们要管理自己的padding$padded_data=$data。str_repeat('',16-(strlen($data)%16));$encrypted=openssl_encrypt($padded_data,$method,$password,OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING,$iv);$output=openssl_decrypt($encrypted,$method,$password,OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING,$iv);var_dump(rtrim($输出));(注:如上OPENSSL_ZERO_PADDING并不是用0填充)由此我们可以解释为什么在第一个例子中openssl_encrypt之前加了自主点计费\x00的代码从上面的加解密,填充逻辑是不同的,上面的例子可以很好的解释:例子1:mcrypt在加密的时候没有使用padding,所以用\x00填充,所以用openssl替换的时候,需要独立实现\x00padding。例2:mcrypt在加密的时候使用的是standardpadding,openssl的padding方式也是standardpadding,所以可以直接使用。分析到这里可以发现,无论使用什么padding策略,都需要注意加密时加padding,解密时去掉padding。至此,上面例子中的填充相关性分析就完成了,接下来我们看一下如何选择被替换的算法。算法选择上面的例子中有一个问题,如何将mcrypt中的AES-128-CBC算法替换成openssl中的AES_256?关于这一点,我还没有找到合理的解释。查了一会儿源码。没找到原因(能力有限~),但是通过下面的信息,函数openssldecryptsmcryptAESdataincompatibleproblemConvertmcrypt_generictoopenssl_encrypt问问题如果有人找到原因,请给我留言,谢谢。总结对于使用mcryptAES的加密部分,如果替换过程中出现问题,可以从算法替换或者padding两个方面考虑。同时,必须满足的条件是根据不同的填充方式进行选择。替换时最需要考虑的就是考虑兼容性问题,保证替换后不会发生变化。虽然只有一点点区别——最后几个字符串的区别,在多个平台上同时修改是一件很麻烦的事情,但是改动越少,风险就越小。本文只是对AES算法的简单介绍,是否适用于其他算法还有待研究。参考PHP7.1.x中弃用的功能:http://www.php.net/manual/zh/migration71.deprecated.phpmcrypt扩展已弃用:http://www.php.net/manual/zh/book。mcrypt.phpAES算法:https://blog.csdn.net/qq_28205153/article/details/55798628mcrypt源码:https://github.com/php/php-src/blob/php-7.0.30/ext/mcrypt/mcrypt.copenssl扩展原代码:https://github.com/php/php-src/blob/master/ext/openssl/openssl.copenssl解密mcryptAES数据不兼容问题:https://www.v2ex.com/t/370493将mcrypt_generic转换为openssl_encrypt问问题:https://stackoverflow.com/questions/48800725/convert-mcrypt-generic-to-openssl-encrypt