顽皮的mcrypt加密函数类

作者: xahy 分类: 笔记 发布时间: 2019-07-29 18:45

在看一个17年左右的项目源码,

碰到了一个这样的加密类。

<?php
namespace app\common\lib;

/***
 * aes 加密 解密类库
 * Class Aes
 * @package app\common\lib
 */
class Aes {

    private $key = null;

    /***
     * 密钥
     * Aes constructor.
     */
    public function __construct() {
        $this->key = '1234567887654321';
    }

    /**
     * 加密
     * @param String input 加密的字符串
     * @param String key   解密的key
     * @return HexString
     */
    public function encrypt($input = '') {
        $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
        $input = $this->pkcs5_pad($input, $size);
        $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        mcrypt_generic_init($td, $this->key, $iv);

        $data = mcrypt_generic($td, $input);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        $data = base64_encode($data);

        return $data;

    }
    /**
     * 填充方式 pkcs5
     * @param String text      原始字符串
     * @param String blocksize   加密长度
     * @return String
     */
    private function pkcs5_pad($text, $blocksize) {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }

    /**
     * 解密
     * @param String input 解密的字符串
     * @param String key   解密的key
     * @return String
     */
    public function decrypt($sStr) {
        $decrypted= mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$this->key,base64_decode($sStr), MCRYPT_MODE_ECB);
        $dec_s = strlen($decrypted);
        $padding = ord($decrypted[$dec_s-1]);
        $decrypted = substr($decrypted, 0, -$padding);

        return $decrypted;
    }

}

 

可以看的出,
Aes 这个加密类使用的是mcrypt系列的加密函数。
同时也知道,
mcrypt系列的加密函数从7.1开始已经被废弃,
所以需要使用openssl系列加密函数替代mcrypt系列的加密函数。

 

要想转变为openssl系列加密函数,

得注意 密钥(key)、初始向量(iv)和加密模式($raw_output),

这样就能转换为 openssl 加密。

  • 关于密钥

密钥很容易就能从源代码类中得到,对应的是 $key 属性值。

  • 关于初始向量

这个是源码比较顽皮的部分,

哈哈哈哈。

开始被这句话混淆到了 $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);

认为 $iv 值是通过 mcrypt_create_iv 函数生成的,

所以转换 openssl 代码也需要通过 openssl 函数生成。

对应代码:$ivlen = openssl_cipher_iv_length($cipher);$iv = openssl_random_pseudo_bytes($ivlen);

然而本地运行却发现,

得到的结果根本不一致。

 mcrypt 函数加密类多次运行会发现返回的加密后的字符串一直保持不变的

而openssl 函数加密类如果改变 iv 值加密后的字符串一直变化的

只能再回头看看源码了,

通过 Aes 类不难发现 encrypt 这个方法 其实调用的就是 ECB 加密模式,

而Mcrypt 函数是有对应的 mcrypt_ecb 函数使用 ECB 模式加解密数据,

虽然已经废弃,

但是不影响 test。

参考函数给出的代码例子,

得到  encryptString 加密函数:

    /// 参考资料:https://www.php.net/manual/zh/function.mcrypt-cbc.php
    function encryptString($data) {
//        $iv = '';
        $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
        $input = $this->pkcs5_pad($data, $size);
        $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND);
        $enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->key, $input, MCRYPT_MODE_ECB, $iv);
        return base64_encode($enc);
    }

上面的 $iv 的 值无论是生成的还是空的,

得到的加密后的字符串都是一致且保持不变的

至此,可以断定 iv 的实际值就是空字符串了。

  • 关于加密模式

通过 MCRYPT_MODE_ECB 常量可以得知,

加密模式使用的是 ecb,

在 openssl 加密函数中对应的就是 aes-128-ecb。

现在已经确定了密钥、向量和加密模式,

那么对应的 openssl 加密类,

就可以转换出来了:

<?php
namespace app\common\lib;


/****
 * openssl方式加密代替mcrypt
 * Class OpenAce
 * @package app\common\lib
 */
class OpenAes {

    private $cipher = 'aes-128-ecb';
    private $key = '1234567887654321';
    private $iv = '';


    /***
     * 加密
     * @param $data 需要加密的字符串
     * @return string
     */
    public function opensslC($data){
        return base64_encode(openssl_encrypt($data, $this->cipher, $this->key, OPENSSL_RAW_DATA , $this->iv));
    }

    /***
     * 解密
     * @param $str 加密后的字符串
     * @return string
     */
    public function opensslD($str){
        return openssl_decrypt(base64_decode($str),$this->cipher,$this->key,OPENSSL_RAW_DATA,$this->iv);
    }

}

 

测试时使用到的测试类,包含对 mcrypt、openssl 的测试。

<?php
namespace app\api\controller;

use app\common\lib\Aes;
use app\common\lib\OpenAes;
use think\Controller;


class Test extends Controller {


    ///测试 mcrypt 加密
    public function encrypt(){
        $data = [
            'did' => '我在人民广场吃炸鸡',
            'version' => 1,
            'time' => '1564384941568',
        ];

        // 1 按字段排序
        ksort($data);
        // 2拼接字符串数据  &
        $string_data = http_build_query($data);
        // 3通过aes来加密
        $string = (new Aes())->encrypt($string_data);

        echo "<pre>";
        //vBhbc+ylzoSk7Wg1z/VvUvye4tM3XhQJAM9rD40GJLw1+MqLVQ+RGW99fKDBi2cU1LeWzprOo+Pf76DFqQQ/ZSihiw+niikwEu/fOv0FvkbUq3UGfQjZRjQRlfhXAerstFEinQjveRFNnN/ZHYQc8XC+0FQxBirUaFGFBjzEIp0=
        var_dump($string);
        ///did=%E6%88%91%E5%9C%A8%E4%BA%BA%E6%B0%91%E5%B9%BF%E5%9C%BA%E5%90%83%E7%82%B8%E9%B8%A1&time=1564384941568&version=1
        var_dump($string_data);
        exit;


    }


    ///测试 mcrypt 解密
    public function decrypt(){
        $str = 'vBhbc+ylzoSk7Wg1z/VvUvye4tM3XhQJAM9rD40GJLw1+MqLVQ+RGW99fKDBi2cU1LeWzprOo+Pf76DFqQQ/ZSihiw+niikwEu/fOv0FvkbUq3UGfQjZRjQRlfhXAerstFEinQjveRFNnN/ZHYQc8XC+0FQxBirUaFGFBjzEIp0=';
        $decrypt_str = (new Aes())->decrypt($str);

        echo "<pre>";

        ///did=%E6%88%91%E5%9C%A8%E4%BA%BA%E6%B0%91%E5%B9%BF%E5%9C%BA%E5%90%83%E7%82%B8%E9%B8%A1&time=1564384941568&version=1
        var_dump($decrypt_str);
        ///bool(true)
        var_dump($decrypt_str === 'did=%E6%88%91%E5%9C%A8%E4%BA%BA%E6%B0%91%E5%B9%BF%E5%9C%BA%E5%90%83%E7%82%B8%E9%B8%A1&time=1564384941568&version=1');

        exit;
    }



    ///测试 openssl 加密
    function openC(){
        $data = [
            'did' => '我在人民广场吃炸鸡',
            'version' => 1,
            'time' => '1564384941568',
        ];

        // 1 按字段排序
        ksort($data);
        // 2拼接字符串数据  &
        $string_data = http_build_query($data);
        // 3通过openssl来加密
        $string = (new OpenAes())->opensslC($string_data);


        echo "<pre>";

        //opensslC:vBhbc+ylzoSk7Wg1z/VvUvye4tM3XhQJAM9rD40GJLw1+MqLVQ+RGW99fKDBi2cU1LeWzprOo+Pf76DFqQQ/ZSihiw+niikwEu/fOv0FvkbUq3UGfQjZRjQRlfhXAerstFEinQjveRFNnN/ZHYQc8XC+0FQxBirUaFGFBjzEIp0=
        var_dump($string);
        ///opensslC:did=%E6%88%91%E5%9C%A8%E4%BA%BA%E6%B0%91%E5%B9%BF%E5%9C%BA%E5%90%83%E7%82%B8%E9%B8%A1&time=1564384941568&version=1
        var_dump($string_data);
        exit;


    }


    ///测试 openssl 解密
    function openD(){
        $str = 'vBhbc+ylzoSk7Wg1z/VvUvye4tM3XhQJAM9rD40GJLw1+MqLVQ+RGW99fKDBi2cU1LeWzprOo+Pf76DFqQQ/ZSihiw+niikwEu/fOv0FvkbUq3UGfQjZRjQRlfhXAerstFEinQjveRFNnN/ZHYQc8XC+0FQxBirUaFGFBjzEIp0=';
        $decrypt_str = (new OpenAes())->opensslD($str);

        echo "<pre>";

        ///opensslC:did=%E6%88%91%E5%9C%A8%E4%BA%BA%E6%B0%91%E5%B9%BF%E5%9C%BA%E5%90%83%E7%82%B8%E9%B8%A1&time=1564384941568&version=1
        var_dump($decrypt_str);
        ///opensslC:bool(true)
        $original_str = 'did=%E6%88%91%E5%9C%A8%E4%BA%BA%E6%B0%91%E5%B9%BF%E5%9C%BA%E5%90%83%E7%82%B8%E9%B8%A1&time=1564384941568&version=1';
        var_dump($decrypt_str === $original_str);

        exit;
    }






}

 

总结:

  • 对于 mcrypt 转换为 openssl 多注意 密钥(key)、初始向量(iv)和加密模式($raw_output)即可。

 

参考资料:

  • mdecrypt_generic函数:https://www.php.net/manual/zh/function.mdecrypt-generic.php
  • mcrypt_module_open函数:https://www.php.net/manual/zh/function.mcrypt-module-open.php
  • openssl函数:https://www.php.net/manual/en/ref.openssl.php
  • openssl_encrypt函数:https://www.php.net/manual/en/function.openssl-encrypt.php
  • mcrypt-cbc函数:https://www.php.net/manual/zh/function.mcrypt-cbc.php

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注