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