SM4分组密码算法的初始密钥加载与反序变换
我将详细讲解SM4算法中初始密钥加载(Initial Key Loading)与反序变换(Reverse Transformation)这一具体步骤。这是SM4加密和解密过程正确运行的关键基础,尤其体现了其加/解密结构对称性的巧妙设计。
题目描述
SM4是我国商用分组密码标准(GB/T 32907),采用32轮非平衡Feistel结构,分组长度和密钥长度均为128位。算法在加密和解密时,轮密钥的使用顺序是相反的。为实现这一特性,SM4在开始加/解密前,需要对输入的128位主密钥进行“加载”处理,生成一个32字的轮密钥数组 (rk_0, rk_1, ..., rk_31) 供后续使用。同时,解密过程被定义为加密过程的逆序执行,这直接依赖于轮密钥的反序使用。我们需要清晰地理解:初始密钥如何扩展成轮密钥数组,以及在解密时,如何通过简单的“反序”操作来复用加密的轮密钥生成逻辑,而无需实现另一套逆向的密钥扩展算法。
解题过程(详解)
第一步:理解SM4算法的基本输入与输出
- 明文/密文输入:一个128位的分组,在算法内部被等分为4个32位的字,记为
(X_0, X_1, X_2, X_3)。 - 密钥输入:一个128位的主密钥
MK,同样被等分为4个32位的字,记为(MK_0, MK_1, MK_2, MK_3)。 - 目标:我们需要从
MK生成32个轮密钥rk_i(i=0到31),每个rk_i也是32位。
第二步:系统参数与固定常数的引入
SM4定义了两个关键的32位常数序列:
- 固定密钥
FK:FK = (FK_0, FK_1, FK_2, FK_3) = (0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC)。FK用于在密钥扩展的初始阶段与主密钥进行混合,增加算法的复杂性。 - 固定参数
CK:这是一个包含32个32位常数的数组CK_0, CK_1, ..., CK_31。每个CK_i是通过特定方法生成的伪随机常数,在轮密钥生成时作为轮常量引入,确保每轮密钥的差异性。
第三步:初始密钥加载(Initial Key Loading)—— 加密与解密的共同起点
这是密钥扩展的第一步,也是加密和解密过程完全相同的操作。它产生一个中间密钥状态 K = (K_0, K_1, K_2, K_3)。
具体计算过程如下:
- 将128位主密钥
MK分成4个字:MK = (MK_0, MK_1, MK_2, MK_3)。 - 将每个
MK_i与对应的固定密钥FK_i进行逐比特异或(XOR)操作。 - 得到初始的中间密钥状态:
\[ (K_0, K_1, K_2, K_3) = (MK_0 \oplus FK_0, MK_1 \oplus FK_1, MK_2 \oplus FK_2, MK_3 \oplus FK_3) \]
为什么需要FK?
如果没有FK,当主密钥MK为全0时,中间状态K初始也为全0,可能导致轮密钥的随机性不足。FK的引入破坏了这种对称性,增强了算法对弱密钥的抵抗力。
第四步:轮密钥迭代生成(Round Key Iteration)
从初始状态 (K_0, K_1, K_2, K_3) 开始,我们迭代32轮(i = 0, 1, ..., 31),生成32个轮密钥 rk_i。这是加密时的正向生成顺序。
每一轮(第 i 轮)的计算公式如下:
\[K_{i+4} = K_i \oplus T'(K_{i+1} \oplus K_{i+2} \oplus K_{i+3} \oplus CK_i) \]
其中:
T'是一个可逆的合成变换,由非线性变换τ和线性变换L'组成,即T'(·) = L'(τ(·))。τ变换使用4个并行的8进8出S盒,L'是一个线性变换:L'(B) = B ⊕ (B <<< 13) ⊕ (B <<< 23)。CK_i是第 i 轮的固定参数。- 计算得到的
K_{i+4}直接作为第 i 轮的轮密钥,即:
\[ rk_i = K_{i+4} \]
注意观察结构:这个迭代过程很像一个线性反馈移位寄存器(LFSR),但加入了非线性变换 T‘ 和轮常量 CK_i。每轮产生一个新的 K 字(K_4 到 K_35),其中 K_4 到 K_35 正好对应 rk_0 到 rk_31。
第五步:解密时的关键——“反序变换”(Reverse Transformation)
这是SM4设计最精妙的地方之一,它实现了加/解密的对称性。
核心规则:SM4的解密算法与加密算法结构完全相同,唯一的区别在于轮密钥的使用顺序相反。
这意味着:
- 加密时:使用轮密钥序列
(rk_0, rk_1, ..., rk_31)。 - 解密时:使用轮密钥序列
(rk_31, rk_30, ..., rk_0)。
如何实现?
你不需要为解密重新写一套密钥扩展算法。你只需要:
- 在解密开始前,像加密一样,用相同的主密钥
MK执行一遍完全相同的初始密钥加载和轮密钥迭代生成过程(即第三、四步)。这将得到相同的轮密钥数组(rk_0, rk_1, ..., rk_31)。 - 在解密执行时,将生成的轮密钥数组反序提供给解密函数。即第1轮解密使用
rk_31,第2轮使用rk_30,……,第32轮使用rk_0。
为什么可以这样?
因为SM4的轮函数 F 本身是可逆的(得益于Feistel结构和T变换的可逆性)。将加密过程的轮顺序完全颠倒,并配合轮密钥的反序使用,就能精确地逆向执行所有操作,恢复出明文。这种设计极大地简化了硬件和软件实现,因为加密和解密可以共用绝大部分电路或代码。
第六步:总结与流程图示
完整流程(以加/解密程序视角):
输入:128位主密钥 MK
步骤1(初始加载):计算 (K_0, K_1, K_2, K_3) = (MK_0⊕FK_0, MK_1⊕FK_1, MK_2⊕FK_2, MK_3⊕FK_3)
步骤2(迭代扩展):
for i = 0 to 31:
rk[i] = K_{i+4} = K_i ⊕ T‘(K_{i+1} ⊕ K_{i+2} ⊕ K_{i+3} ⊕ CK_i)
end for
步骤3(使用):
- 加密函数:接收轮密钥数组 rk[0..31],按正序 i=0..31 使用。
- 解密函数:接收轮密钥数组 rk[0..31],但按反序 i=31..0 使用。
安全性要点:
FK和CK的引入,确保了即使主密钥具有某种规律性,生成的轮密钥也具有良好的伪随机性和差异性。T‘变换中的非线性S盒和扩散变换L’,使得轮密钥之间具有高度的非线性关系,难以从部分轮密钥恢复主密钥或其他轮密钥。- 反序变换的简单性依赖于轮函数的可逆性和Feistel结构的对称性,这是一种经典且高效的设计范式。
通过以上步骤,我们清晰地剖析了SM4算法中从主密钥到轮密钥的映射关系,以及支撑其加/解密对称性的核心机制——一次正向生成,反序使用。