记录 Openssl 替代 Mcrypt 3DES CBC 算法解决方案

由于php 7.1 以后弃用了Mcrypt算法,现在用openssl替代,经过测验合格

上代码
Mcrypt算法:

class Crypt3Des {

    var $key;//秘钥
    var $iv;//填充向量

    //初始化
    function Crypt3Des($key, $iv){
        $this->key = $key;
        $this->iv = $iv;
    }

    //填充加密快的大小
    function pkcs5_pad ($text, $blocksize) {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }

    //解密
    function decrypt($encrypted){
        $encrypted = pack('H*', $encrypted);
        $key = str_pad($this->key,24,'0');
        $td = mcrypt_module_open(MCRYPT_3DES,'',MCRYPT_MODE_CBC,'');
        if( $this->iv == '' )
        {
            $iv = @mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        }
        else
        {
            $iv = $this->iv;
        }
        //$ks = mcrypt_enc_get_bkey_size($td);
        @mcrypt_generic_init($td, $key, $iv);
        $decrypted = mdecrypt_generic($td, $encrypted);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        $y=$this->pkcs5_unpad($decrypted);
        return $y;
    }

    //加密
    function encrypt($input){
        $size = mcrypt_get_block_size(MCRYPT_3DES,MCRYPT_MODE_CBC);
        $input = $this->pkcs5_pad($input, $size);
        $key = str_pad($this->key,24,'0');
        $td = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_CBC, '');
        if( $this->iv == '' )
        {
            $iv = @mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        }
        else
        {
            $iv = $this->iv;
        }
        echo "key:".$key."</br>";
        echo "iv:".$iv."</br>";
        echo "input:".$input."</br>";
        @mcrypt_generic_init($td, $key, $iv);
        $data = mcrypt_generic($td, $input);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        $data = bin2hex($data);
        return $data;
    }




    function pkcs5_unpad($text){
        $pad = ord($text{strlen($text)-1});
        if ($pad > strlen($text)) {
            return false;
        }
        if (strspn($text, chr($pad), strlen($text) - $pad) != $pad){
            return false;
        }
        return substr($text, 0, -1 * $pad);
    }

}

Openssl替代算法

class Crypt3Des
{

    var $key;//秘钥
    var $iv;//填充向量

    //初始化
    function __construct($key, $iv)
    {
        $this->key = $key;
        $this->iv = $iv;
    }

    //填充加密快的大小
    function pkcs5_pad($text, $blocksize)
    {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }

    //解密
    function decrypt($encrypted)
    {
        $key = str_pad($this->key, 24, '0');
        $encrypted = pack('H*', $encrypted);
        $size = openssl_cipher_iv_length("DES-EDE3-CBC");
        if ($this->iv == "") {
            $iv = openssl_random_pseudo_bytes($size);
        } else {
            $iv = $this->iv;
        }
        $rs = openssl_decrypt($encrypted, "DES-EDE3-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
        $rs = $this->pkcs5_unpad($rs);
        return $rs;
    }

    //加密
    function encrypt($input)
    {
        $key = str_pad($this->key, 24, '0');
        $size = openssl_cipher_iv_length("DES-EDE3-CBC");
        if ($this->iv == "") {
            $iv = openssl_random_pseudo_bytes($size);
        } else {
            $iv = $this->iv;
        }
        $input = $this->pkcs5_pad($input, $size);
        $rs = openssl_encrypt($input, "DES-EDE3-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
        $rs = bin2hex($rs);
        return $rs;
    }


    function pkcs5_unpad($text)
    {
        $pad = ord($text{strlen($text) - 1});
        if ($pad > strlen($text)) {
            return false;
        }
        if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
            return false;
        }
        return substr($text, 0, -1 * $pad);
    }

}
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 1

楼主代码给了,我来给大家简单说一说这个函数 openssl_encrypt 的函数

PHP 7.0 开始就不推荐 Mcrypt 模块(这个模块太旧了,有十多年了,早就不安全了,而且有些算法名不太兼容事实上的密码套件 openssl)

首先上面的 openssl_encrypt/decrypt 为对称加密函数,优点是速度快,arm 芯片也能胜任大部分大部分对称加密函数的计算。

另外楼主的加密算法 DES-EDE3-CBC 为不推荐,不安全的对称加密算法,建议切换。目前对称加密算法(tls 1.2 标准)比较常用的 AES 算法。

其中算法后面的 CBC 三个字母为填充模式。加密过程为分块加密,即一个块一个块的加密,块越大,运算越复杂。

以 AES-256-CBC 算法的举例:即使用 AES(高级对称加密)- 256(分成 256 bit 一块一块的来加密,对应的密钥为 256 bit 即 32 为字符)-CBC(为填充模式,像上面的 pkcs5 填充即在,最后一块不够 256 bit 时填 0,pkcs7 则为块长 - 剩余,即最后剩了 1 byte 的话,则填充为 07,07,07,07,07,07,07。(8 - 1))。另外加密完后后时返回的是 byte,所以必须转成 hex 或 base64 才能传输。而以上这些填充,转成 base64 这些操作,openssl_encrypt 函数都自带了。所以

//  option = 0 ,自动进行 pkcs7 填充(pkcs5 是 7 的子集,兼容),并返回 base64 格式。解密同样
openssl_encrypt($originString, "AES-256-CBC", $_256bitkey, 0, $iv);

另外加密这种一般会与别的语言进行交互,所以你要确保其他语言能解密,最好 iv 两边确定,只传输 key,贴一段 java 的解密

public static String decrypt(String encrypted) {
    try {
        SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(StandardCharsets.UTF_8), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, IV);
        byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
        return new String(original);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

另外直接升级 https 可以解决很多问题,https 的 tls 安全套件的算法更牛逼,1.2,1.3 基本能做到,一次消息一个密钥(截获了,解了只对应当前消息,即这条消息的密钥,不能解下条消息),椭圆曲线算法性能也更高,很多 CPU 指令都有对应优化

3年前 评论

楼主代码给了,我来给大家简单说一说这个函数 openssl_encrypt 的函数

PHP 7.0 开始就不推荐 Mcrypt 模块(这个模块太旧了,有十多年了,早就不安全了,而且有些算法名不太兼容事实上的密码套件 openssl)

首先上面的 openssl_encrypt/decrypt 为对称加密函数,优点是速度快,arm 芯片也能胜任大部分大部分对称加密函数的计算。

另外楼主的加密算法 DES-EDE3-CBC 为不推荐,不安全的对称加密算法,建议切换。目前对称加密算法(tls 1.2 标准)比较常用的 AES 算法。

其中算法后面的 CBC 三个字母为填充模式。加密过程为分块加密,即一个块一个块的加密,块越大,运算越复杂。

以 AES-256-CBC 算法的举例:即使用 AES(高级对称加密)- 256(分成 256 bit 一块一块的来加密,对应的密钥为 256 bit 即 32 为字符)-CBC(为填充模式,像上面的 pkcs5 填充即在,最后一块不够 256 bit 时填 0,pkcs7 则为块长 - 剩余,即最后剩了 1 byte 的话,则填充为 07,07,07,07,07,07,07。(8 - 1))。另外加密完后后时返回的是 byte,所以必须转成 hex 或 base64 才能传输。而以上这些填充,转成 base64 这些操作,openssl_encrypt 函数都自带了。所以

//  option = 0 ,自动进行 pkcs7 填充(pkcs5 是 7 的子集,兼容),并返回 base64 格式。解密同样
openssl_encrypt($originString, "AES-256-CBC", $_256bitkey, 0, $iv);

另外加密这种一般会与别的语言进行交互,所以你要确保其他语言能解密,最好 iv 两边确定,只传输 key,贴一段 java 的解密

public static String decrypt(String encrypted) {
    try {
        SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(StandardCharsets.UTF_8), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, IV);
        byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
        return new String(original);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

另外直接升级 https 可以解决很多问题,https 的 tls 安全套件的算法更牛逼,1.2,1.3 基本能做到,一次消息一个密钥(截获了,解了只对应当前消息,即这条消息的密钥,不能解下条消息),椭圆曲线算法性能也更高,很多 CPU 指令都有对应优化

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!