SM4算法是中国国家密码管理局发布的商用密码算法标准,具有高安全性、高效率、易实现和兼容性强等特点。它广泛应用于通信加密、数据存储加密、网络安全、隐私保护和安全认证等领域,成为保障我国信息安全的重要基石。
一、介绍
SM4算法,全称为SM4分组密码算法,是由中国国家密码管理局发布的商用密码算法标准。它是一种分组密码算法,采用Feistel结构,密钥长度为128位,分组长度为128位。SM4算法具有较高的安全性和效率,适用于多种场景下的数据加密与解密。
在密码学中,分组加密(英语:Block cipher),又称分块加密或块密码,是一种对称密钥算法。它将明文分成多个等长的模块(block),使用确定的算法和对称密钥对每组分别加密解密。分组加密是极其重要的加密协议组成,其中典型的如DES和AES作为美国政府核定的标准加密算法,应用领域从电子邮件加密到银行交易转帐,非常广泛。
国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。
加密类型 | 加密算法 | 应用范围 | 对应国际加密算法 | 说明 |
---|---|---|---|---|
非对称加密 | SM2 | 身份认证、数字签名、密码交换、256位椭圆曲线 | RSA、RSA4096 | 需要生成秘钥对(公钥和私钥 |
对称加密算法 | SM1 | 128位数字加密、算法不公开、仅以IP核的形式存在于芯片中。智能IC卡、智能密码钥匙、加密卡、加密机 | DES、3DES、AES(128)、AES192、AES256 | AES是取代DES的算法 |
SM4 | 128位数据加密、相当于AES(128) | |||
完整性运算 | SM3 | 256位数据摘要计算,相当于SHA256 | SHA1、SHA-256、SHA-384、SHA-512 |
二、原理
SM4 算法主要包含异或、移位以及盒变换操作。它分为密钥拓展和加/解密两个模块,这两个模块的流程大同小异
其中,移位变换是指循环左移;盒变换是一个将8bit输入映射到8bit输出的变换,是一个固定的变换
下图是 SM4 的加解密(左)和密钥拓展(右)的流程图
(1) 加解密
- 输入的明文为 128bit 的数据,将其按位拆分成 4 个 32bit 的数据 x0,x1,x2,x3x0,x1,x2,x3
当 i=0i=0 时为第一次轮变换,一直进行到 i=31i=31 结束 - xixi 暂时不做处理,将 xi+1,xi+2,xi+3xi+1,xi+2,xi+3 和轮密钥 rkirki 异或得到一个 32bit 的数据,作为盒变换的输入
即 sbox_input=xi+1⊕xi+2⊕xi+3⊕rkisbox_input=xi+1⊕xi+2⊕xi+3⊕rki,⊕⊕ 符号代表异或运算 - 将 sbox_inputsbox_input 拆分成 4 个 8bit 数据,分别进行盒变换,之后再将 4 个 8bit 输出合并成一个 32bit 的 sbox_outputsbox_output
- 将刚才获得的 sbox_outputsbox_output 分别循环左移 2,10,18,24 位,得到 4 个 32bit 的结果,记移位结果为 y2,y10,y18,y24y2,y10,y18,y24
- 将移位的结果 y2,y10,y18,y24y2,y10,y18,y24 与盒变换输出 sbox_outputsbox_output 和 xixi 异或,得到 xi+4xi+4
即 xi+4=sbox_output⊕y2⊕y10⊕y18⊕y24⊕xixi+4=sbox_output⊕y2⊕y10⊕y18⊕y24⊕xi - 至此完成了一轮的加解密运算
在实际加解密过程中,上述运算要执行 32 轮,同时使用 32 个不同的 rkirki,rkirki 由密钥拓展生成 - 最后将生成的最后 4 个 32bit 数据 x35,x34,x33,x32x35,x34,x33,x32 合并成一个 128bit 的数据 outputoutput,作为最后的输出结果
(2) 密钥拓展
- 密钥拓展的过程和加解密大同小异
- 输入的原始密钥 keykey 为 128bit 的数据,将其按位拆分成 4 个 3232bit 的数据 K0,K1,K2,K3K0,K1,K2,K3
- 将初始密钥 K0,K1,K2,K3K0,K1,K2,K3 分别异或固定参数 FK0,FK1,FK2,FK3FK0,FK1,FK2,FK3 得到用于循环的密钥 k0,k1,k2,k3k0,k1,k2,k3
即 k0=K0⊕FK0,k1=K1⊕FK1,k2=K2⊕FK2,k3=K3⊕FK3k0=K0⊕FK0,k1=K1⊕FK1,k2=K2⊕FK2,k3=K3⊕FK3 - 进入轮密钥 rkirki 的生成
当 i=0i=0 时为第一次轮变换,一直进行到 i=31i=31 结束 - kiki 暂时不做处理,将 ki+1,ki+2,ki+3ki+1,ki+2,ki+3 和固定参数 CKiCKi 异或得到一个 32bit 的数据,作为盒变换的输入
即 sbox_input=ki+1⊕ki+2⊕ki+3⊕ckisbox_input=ki+1⊕ki+2⊕ki+3⊕cki - 将 sbox_inputsbox_input 拆分成 4 个 8bit 数据,分别进行盒变换,之后再将 4 个 8bit 输出合并成一个 32bit 的 sbox_outputsbox_output
- 将刚才获得的 sbox_outputsbox_output 分别循环左移 13,23 位,得到 2 个 32bit 的结果,记移位结果为 y13,y23y13,y23
- 将移位的结果 y13,y23y13,y23 与盒变换输出 sbox_outputsbox_output 和 kiki 异或,得到 ki+4ki+4
即 rki=ki+4=sbox_output⊕y13⊕y23⊕kirki=ki+4=sbox_output⊕y13⊕y23⊕ki - 至此完成了一轮的加解密运算
在实际加解密过程中,上述运算要执行 32 轮,同时使用 32 个不同的 CKiCKi,CKiCKi 为固定参数 - 执行完 32 轮后,便可获得 32 个用于加解密的 rkirki
(3) SM4 的逆运算
上文介绍了 SM4 的加密和密钥拓展部分,如果想要实现解密,一个简单的方法是将轮密钥 rkirki 逆序后再执行一次 32 轮的加密运算
即将密文投入加密函数,并且第 0 轮使用 rk31rk31 作为轮密钥,第 i 轮使用 rk31−irk31−i 作为轮密钥,最后获得的结果便是加密前的密文
3.1 SM4 加密流程
先观察一下 SM4 加密的结构
-
令 SM4 的轮函数 F(xi,xi+1,xi+2,xi+3,rki)=xi⊕T(xi+1⊕xi+2⊕xi+3⊕rki)F(xi,xi+1,xi+2,xi+3,rki)=xi⊕T(xi+1⊕xi+2⊕xi+3⊕rki),其中函数 TT 包括上述提到的盒变换和移位异或运算。那么 xi+4=F(xi,xi+1,xi+2,xi+3,rki)xi+4=F(xi,xi+1,xi+2,xi+3,rki)
-
以最后一轮加密运算为例,x35=x31⊕T(x32⊕x33⊕x34⊕rk31)x35=x31⊕T(x32⊕x33⊕x34⊕rk31),得到的 4 个 32 bit 字为 x32,x33,x34,x35x32,x33,x34,x35。最后输出会将这 4 个字逆序,即输出 x35,x34,x33,x32x35,x34,x33,x32
3.2 SM4 解密流程
接下来看解密,记加密的明文为 x0,x1,x2,x3x0,x1,x2,x3,将加密算法最后输出结果 x35,x34,x33,x32x35,x34,x33,x32 记为 x′0,x′1,x′2,x′3x0′,x1′,x2′,x3′,并让 rk′i=rk31−irki′=rk31−i (也就是轮密钥逆序)

将 x′0,x′1,x′2,x′3x0′,x1′,x2′,x3′ 和 rk′irki′ 投入加密算法中,观察会发生什么结果
- 对于第 0 轮生成的 x′4=x′0⊕T(x′1⊕x′2⊕x′3⊕rk′0)x4′=x0′⊕T(x1′⊕x2′⊕x3′⊕rk0′),我们知道 x′0=x35,x′1=x34,x′2=x33,x′3=x32x0′=x35,x1′=x34,x2′=x33,x3′=x32,故 x′4=x35⊕T(x34⊕x33⊕x32⊕rk31)=x31⊕T(⋯)⊕T(⋯)=x31x4′=x35⊕T(x34⊕x33⊕x32⊕rk31)=x31⊕T(⋯)⊕T(⋯)=x31
- 类似的,我们可以得出 x′i=x35−ixi′=x35−i,最后一轮函数结束后的结果为 x3,x2,x1,x0x3,x2,x1,x0,经过逆序后便是 x0,x1,x2,x3x0,x1,x2,x3,刚好是加密前的明文,完成了解密操作
通过 SM4 逆运算的过程,我们可以体会到 SM4 最后将结果逆序输出的巧妙之处
三、PHP实现过程
3.1 安装tp5扩展
composer require evit/php-gm-crypto
3.2 实现代码
<?php
// +----------------------------------------------------------------------
// | Copyright (c) 中犇科技 All rights reserved.
// +----------------------------------------------------------------------
namespace app\automatic\service\m1936;
use app\automatic\model\m1936\User as UserModel;
use app\spread\model\FreeProperty;
use service\Format;
use think\App;
use app\spread\controller\Free as Base;
use app\spread\model\FreeTable;
use app\spread\model\SpreadBtnConfig;
use think\Db;
use think\helper\Hash;
use Evit\PhpGmCrypto\Encryption\EvitSM4Encryption;
class Requestapi {
private $config = [
'mode' => 'cbc', // 加密模式,支持 'cbc' 或 'ecb',默认是 'cbc'
'key' => '', // 密钥,长度应为16字节(128位)
'iv' => '', // 初始化向量(IV),长度为16字节
'hash' => false // 是否对密钥和 IV 进行 MD5 哈希处理
];
public function __construct($config = null) {
$config = $this->config;
$config['key'] = hex2bin($config['key']);
$config['iv'] = hex2bin($config['iv']);
$this->config = $config;
}
/**
* SM4加密
*/
public function sm4_encrypt($plaintext) {
$sm4 = new EvitSM4Encryption($this->config);
// 加密过程
$encrypted = $sm4->encrypt($plaintext);
if ($encrypted === false) {
return false;
}
return strtoupper(bin2hex($encrypted));
}
/**
* SM4解密
*/
public function sm4_decrypt($plaintext) {
$sm4 = new EvitSM4Encryption($this->config);
// 解密过程
$decrypted = $sm4->decrypt(hex2bin($plaintext));
if ($decrypted === false) {
return false;
}
return $decrypted;
}
}