SM4加密的详解和使用

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 的加解密(左)和密钥拓展(右)的流程图

pic

(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 (也就是轮密钥逆序)

扫描二维码关注公众号,回复: 17538932 查看本文章

将 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;
    }
    
}