頑皮的mcrypt加密函數類

在看一個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)即可。

原文地址:https://www.xianhenyuan.com/archives/315

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章