道者编程

sm4加密解密

一:SM4算法是我国自主设计的分组对称密码算法,用于实现数据的加密/解密运算,以保证数据和信息的机密性。SM4算法的分组长度和密钥长度均为128比特,加密算法与密钥扩展算法都采用32轮非线性迭代结构,数据解密和数据加密的算法结构相同,只是轮密钥的使用顺序相反,解密轮密钥是加密轮密钥的逆序。

二:SM4算法支持两种加密ECB和CBC:

1:ECB模式。电子密码本模式,最古老、最简单的模式,将加密的数据分成若干组,每组的大小跟加密密钥相同,不足的部分进行填充。按照顺序将计算所得的数据连在一起即可,各段数据之间互不影响。优点:简单、有利于并行计算、误差不会被传递;缺点:不能隐藏明文的模式、可能对明文进行主动攻击。
2:CBC模式。密文分组链接模式,也需要进行分组,不足的部分按照指定的数据进行填充。需要一个初始化向量,每个分组数据与上一个分组数据加密的结果进行异或运算,最后再进行加密。将所有分组加密的结果连接起来就形成了最终的结果。优点:不容易进行主动攻击、安全性好于ECB;缺点:不利于并行计算、误差传递、需要初始化向量。

一般情况下,我们用ECB。

三:CBC模式iv

CBC模式多了一个参数IV,又叫初始化向量(IV),长度为16。

四:开干

这里基于php的openssl_encrypt,非常简单、方便

1:首先看看php内置方法openssl_get_cipher_methods是否支持SM4

print_r(openssl_get_cipher_methods());

 发现是支持的,我们上面好像说的不对,看起来这支持5种模式,我日,不管了,只搞ECB和CBC模式

2:如果我们要支持CBC模式,需要一个IV,这个IV我们可以用php内置方法生成一个:

$ivlen = openssl_cipher_iv_length('sm4-cbc'); //返回iv的长度
$iv = bin2hex(openssl_random_pseudo_bytes($ivlen)); // 返回IV

 我这里$iv = '6b0709bf027bb1070bd924c44791607b'

3:封装类

有些返回base64,有些返回16进制,这里我们两种都支持,默认base64

<?php
class Sm4{

    private $key; //密钥,这个密钥如果对接方规定base64,那么自己转一下
    private $method = 'sm4-ecb'; //加密算法和模式
    private $mode = 'ecb'; //加密模式
    private $iv = ''; //CBC模式初始化向量,ECB模式用不上
    private $resp = 'base64'; // 加密数据返回 base64或者十六进制值

    /**
     * 如果返回base64,默认即可,返回十六进制,设置 resp=16
     */
    public function __construct($key,$resp='base64'){
        $this->key = $key;
        $this->resp = $resp;
    }

    /**
     * 设置模式-默认ecb
     */
    public function mode($mode,$iv){
        $this->mode = $mode;

        $method = 'sm4-'. $mode;
        $this->method = $method;
        $this->iv = $iv;
    }

    /**
     * 加密
     * openssl_encrypt 这里要写OPENSSL_RAW_DATA,不能写OPENSSL_ZERO_PADDING
     * 对于SM4加密算法,OPENSSL_ZERO_PADDING标志是不适用的。SM4算法要求输入数据的长度必须是16字节(128位)的整数倍,并且不需要进行填充。
     */
    public function encrypt($data){
        if($this->mode == 'ecb'){
            $iv = '';
        }else{
            $iv = substr($this->iv, 0, openssl_cipher_iv_length($this->method));
        }
        
        $data = openssl_encrypt($data, $this->method, $this->key, OPENSSL_RAW_DATA, $iv);
        if($this->resp == 'base64'){
            return base64_encode($data); //返回base64
        }else{
            return bin2hex($data); // 返回十六进制
        }
    }

    /**
     * 解密
     */
    public function decrypt($data){
        if($this->resp == 'base64'){
            $data = base64_decode($data);
        }else{
            $data = hex2bin($data);
        }
        
        if($this->mode == 'ecb'){
            $iv = '';
        }else{
            $iv = substr($this->iv, 0, openssl_cipher_iv_length($this->method));
        }
        return openssl_decrypt($data, $this->method, $this->key, OPENSSL_RAW_DATA, $iv);
    }
}

 4:调用

//用CBC模式试试 加密字符串返回base64
$sm4 = new Sm4('0123456789abcdef');
$sm4->mode('cbc','6b0709bf027bb1070bd924c44791607b');
$data = '台湾是中国的';
$encrypted = $sm4->encrypt($data);
echo '原始数据:' . $data . PHP_EOL;
echo '加密后:' . $encrypted . PHP_EOL;
echo '解密后:' . $sm4->decrypt($encrypted) . PHP_EOL;
 输出:

原始数据:台湾是中国的
加密后:cPS7mrnZyMOq0s9DRN7MRik+NDrdnECDuNQOMBp2SHM=
解密后:台湾是中国的
 
//用CBC模式试试 加密字符串返回16进制
$sm4 = new Sm4('0123456789abcdef','16');
$sm4->mode('cbc','6b0709bf027bb1070bd924c44791607b');
$data = '台湾是中国的';
$encrypted = $sm4->encrypt($data);
echo '原始数据:' . $data . PHP_EOL;
echo '加密后:' . $encrypted . PHP_EOL;
echo '解密后:' . $sm4->decrypt($encrypted) . PHP_EOL;

 输出:

原始数据:台湾是中国的
加密后:70f4bb9ab9d9c8c3aad2cf4344decc46293e343add9c4083b8d40e301a764873
解密后:台湾是中国的
 
//用ECB模式试试 加密字符串返回base64
$sm4 = new Sm4('0123456789abcdef');
$data = '台湾是中国的';
$encrypted = $sm4->encrypt($data);
echo '原始数据:' . $data . PHP_EOL;
echo '加密后:' . $encrypted . PHP_EOL;
echo '解密后:' . $sm4->decrypt($encrypted) . PHP_EOL;

 输出:

原始数据:台湾是中国的
加密后:znbWEtCYvIO22zsgZsn3yG2WEzndDEEbiAhgR+q4K+U=
解密后:台湾是中国的
 这里有个工具,我们可以辅助校验:https://lzltool.cn/SM4



最新评论:
我要评论:

看不清楚