SM4加密算法详细解读

Sher10ck 发布于 2026-03-26 218 次阅读


SM4介绍

SM4采用分组加密的方式,明文与密文长度均为128bit(16字节),秘钥也为128bit(16字节),采用32轮迭代,每一轮都需要用到新的轮密钥,即加密算法是对明文的32次加密和对轮密钥的生成。

加密算法介绍

轮函数加密

SM4的分组长度为4字,因此,每一段长度都为4字节,我们用X0​,X1​,X2​,X3​表示,经过加密后,得到的输出是4字的密文,我们用Y0​,Y1​,Y2​,Y3表示,这个加密过程分为两步,由32次轮迭代1次反序变换组成。如图所示是加密流程图:

我们先对加密过程进行系统化介绍:

  • 将输入的128bit拆分成4个4字节的变量X0,X1,X2,X3
  • 进行32次循环,i从0增加到31
  • 将Xi单独取出用于最后一步,Xi+1 Xi+2 Xi+3 与传入的第一轮轮密钥rki进行异或操作作为s盒输入,即sbox-input= x i + 1 ⊕ x i + 2 ⊕ x i + 3 ⊕ r k i (32bit/4字节)
  • 将sbox-input进行拆分,分为4个单字节(8bit)的变量与s盒进行置换,置换后再合并成32bit数据
  • 我们将输出出来的数据分别进行左移位数为(2、10、0、18、24)
  • 我们将左移后的数据分别进行标注为y2,y10,y0,y18,y24,此时我们将左移的5个数据与Xi异或得到Xi+4
  • 这里提到的循环左移是指这个公式,并非补0操作ROL(x, n) = ((x << n) | (x >> (32 - n))) & 0xffffffff
  • L(B)=B⊕(B<<2)⊕(B<<10)⊕(B<<18)⊕(B<<24)(官方公式)
  • xi+4=y0⊕y2⊕y10⊕y18⊕y24⊕xi
  • 循环一共执行32次,最终将x35,x34,x33,x32 合并成一个 128bit 的数据 ,作为最后的输出结果
  • 最后得到的密文是按照X35 || X34 || X33 || X32的顺序输出,即X35在高位,X32在低位

总结单次加密过程:

1、后3位与秘钥进行异或

2、异或后结果单字节进行s盒置换

3、置换后结果左移(2、10、0、18、24)后与Xi异或得到Xi+4

轮密钥生成

轮密钥生成与函数加密过程大同小异

  • 先对128位的key进行拆分成4个4字节密钥记为MK0、MK1、MK2、MK3
  • 拆分采用高位 ---------------------------------> 低位 MK0(高位) MK1 MK2 MK3(低位),构造数组的时候是MK0一般放在0-3,方便在后续进行运算
  • 将拆分出的秘钥分别与固定参数FK1234进行异或得到K1234,
    即 k0=K0⊕FK0,k1=K1⊕FK1,k2=K2⊕FK2,k3=K3⊕FK3,后续统一用i表示
  • 将Ki取出用于最后一步,其他的K与固定参数CKi进行异或得到sbox-input
    sbox_input=ki+1⊕ki+2⊕ki+3⊕cki
  • 之后将4字节输入分成4个单字节输入然后与S盒进行置换,置换后进行拼接得到sbox-output
  • 将sbox-output分别左移(13、0、23)后得到y0、y13、y23,与Ki进行异或得到Ki+4(第i位轮密钥)
  • rki=ki+4=sbox_output⊕y13⊕y23⊕ki
  • 执行完 32 轮后,便可获得 32 个用于加解密的 rki

总结轮密钥生成过程:

1、先将MK与FK分别异或得到Ki

2、将Ki+123与固定参数CKi异或得到s盒输入,并进行进行s盒置换

3、将输出结果左移(13、0、23)后与Ki进行异或得到rki轮密钥

解密

解密过程和加密过程采用相同的方式,唯一不同的是,解密时使用的轮密钥是先用rk31,最后再用rk0

注意点:

在最近做SM4的题目时发现SM4的读取方式是一个需要十分注意的点,正常的X86架构下,数据读取都是小端读取,而SM4在加密过程中数据读取是按照大端读取的,最终的结果也是大端拼接,这么说可能有些生硬,我们直接看例子:

假设条件:明文 = 1234567890ABCDEF
密钥 = AABBCCDDEEFFGGHH

按 SM4 的大端分组,变成:

X0 = 0x31323334
X1 = 0x35363738
X2 = 0x39304142
X3 = 0x43444546
但是我们X86读取的方式是X0 = 0x34333231刚好相反,所以加入一端修正进行大端改写

秘钥跟密文同理

最终我们得到密文为:

Y0 = 0xCA752DEB
Y1 = 0xCFEB00CA
Y2 = 0xB4437301
Y3 = 0x0AAE0D1D

输出的时候密文用CA 75 2D EB CF EB 00 CA B4 43 73 01 0A AE 0D 1D拼接出去

所以还会有一段对于密文输出的修正段,此时我们对于SM4大小端的理解就彻底搞懂了

如果要解密,我们拿到的密文,依旧举例看怎么去输入秘钥密文,可以看到秘钥是按小端存储的,此时如果没有对秘钥进行的操作则我们的秘钥直接抄进去即可,最好按照汇编层去抄写,每组4字节,但如果秘钥也进行了前面明文所做的交换操作,则也需要进行倒序输入

密文层面我们前面知道密文做了大小端互换,那么这里我们每四字节也要进行大小端交换,所以输入密文应为b5 c8 ac cf ea 52 4d e3 70 2b 1f 08 3d 47 61 6b

还有种方法,SM4如果没有魔改,那么我们输入进SM4的数据都应该是大端读取之后的,可以看到我们对明文读取和密文读取都做了大小端转换,秘钥没有做大小端转换,那么可以知道内存中我们的秘钥本身就是大端存储的,所以直接输入进秘钥段就可以了,密文做一下大小端交换输入进去也就可以得到明文了

此作者没有提供个人介绍。
最后更新于 2026-04-11