SM2(国密2)算法暂时不可用,SM4(国密4)可以使用(有多种模式)
/**
* @description: SM(国密)算法
*/
class SMAlgorithm
{
/**
* @description:
* @var array 加解密所需数据
*/
protected $data = [];
/**
* @description:
* @var string 加解密方法名
*/
protected $func;
/**
* @description: 秘钥长度
* @var array
*/
protected $keyLength = [
'sm4' => 16,
];
/**
* @description: 加密模式对应iv长度
* @var array
*/
protected $ivLength = [
'ecb' => 0,
'cbc' => 16,
'cfb' => 16,
'ofb' => 16,
'ctr' => 16,
];
/**
* @description: SM加解密
* @param {string} $data 需要加解密的数据
* @param {string} $algotithm 加密算法
* @param {string} $key 加密key
* @param {int} $options 加密填充 OPENSSL_RAW_DATA/OPENSSL_ZERO_PADDING 不填充/以0填充
* @param {string} $iv 初始化向量,ecb模式不需要,其他模式需要,且长度不一
* @param {bool} $isDe 加密/解密
* @return {*}
*/
public function encryptDecrypt(string $data, string $algotithm, string $key, int $options = OPENSSL_RAW_DATA, string $iv = '', bool $isDe = false):string
{
list($sm, $mode) = explode('-', $algotithm, 2);
$keylength = $this->keyLength[strtolower($sm)] ?? false;
if (!$keylength) {
throw new \Exception('SM加密长度错误');
}
$this->func = 'openssl_' . ($isDe ? 'decrypt' : 'encrypt');
// 获取算法对应的iv长度
$ivLength = openssl_cipher_iv_length($algotithm);
$this->data = [
'data' => $data,
'key' => substr(str_pad($key, $keylength, 0), 0, $keylength),
'options' => $options,
'algotithm' => $algotithm,
'iv' => substr(str_pad($iv, $ivLength, 0), 0, $ivLength),
'isDe' => $$isDe,
];
$modeFunc = '_' . $mode;
if (!method_exists($this, $modeFunc)) {
throw new \Exception('SM加密模式错误');
}
$result = $this->$modeFunc();
return $isDe ? base64_encode($result) : trim($result);
}
/**
* @description: cbc模式,不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。
* 但不利于并行计算,误差会传递,需要初始化向量IV
* @return {*}
*/
private function _cbc():string
{
return $this->_callute(true);
}
/**
* @description: ecb模式,简单,有利于并行计算,误差不会被传送;
* 但不能隐藏明文的模式,可能对明文进行主动攻击
* @return {*}
*/
private function _ecb():string
{
return $this->_callute(false);
}
/**
* @description: cfb模式,隐藏了明文模式,分组密码转化为流模式,可以及时加密传送小于分组的数据;
* 但是不利于并行计算;误差传送:一个明文单元损坏影响多个单元;唯一的IV;
* @return {*}
*/
private function _cfb()
{
return $this->_callute(true);
}
/**
* @description: ofb模式,隐藏了明文模式;分组密码转化为流模式;可以及时加密传送小于分组的数据;
* 但不利于并行计算;对明文的主动攻击是可能的;误差传送:一个明文单元损坏影响多个单元;
* @return {*}
*/
private function _ofb()
{
return $this->_callute(true);
}
/**
* @description: ctr模式,Counter
* 计算器模式不常见,在CTR模式中, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。
* 这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次
* @return {*}
*/
private function _ctr()
{
return $this->_callute(true);
}
/**
* @description: 加解密计算
* @param bool $iv 是否需要定义iv
* @return {*}
*/
private function _callute(bool $iv = false):string
{
if ($iv) {
return $this->$func($this->data['data'], $this->data['algotithm'], $this->data['key'], $this->data['options'], $this->data['iv']);
}
return $this->$func($this->data['data'], $this->data['algotithm'], $this->data['key'], $this->data['options']);
}
/**
* @description: 获取openSSL库的错误,错误消息已被队列化,可以查询到多条错误信息,最后一个是最近的一个错误。
* @param
* @return array
*/
public function errors()
{
$result = [];
while ($data = openssl_error_string() !== false) {
$result[] = $data;
}
return $result;
}
}