SM4 算法
SM4 是中国国家密码管理局发布的对称加密标准(原名 SMS4),主要用于无线局域网标准(WAPI)及各类政务、金融应用。它在结构上属于分组密码算法。
1. 核心规格
- 分组长度:128 位(16 字节)。
- 密钥长度:128 位(16 字节)。
- 轮数:共 32 轮变换。
- 设计结构:非平衡 Feistel 结构(实际上更接近于一种特殊的迭代结构,其解密算法与加密算法结构相同,仅轮密钥顺序相反)。
2. 算法逻辑
SM4 的核心逻辑在于其非线性变换部分。
- S 盒(S-box):SM4 的 S 盒是基于有限域映射的,能够抵抗差分攻击和线性攻击。
- 线性变换(L):用于扩散,确保每一位的变化都能迅速影响到整个分组。
- 轮密钥生成:通过系统参数和固定参数,将 128 位初始密钥扩展出 32 个轮密钥。
OFB 模式:将分组密码变为流密码
OFB (Output Feedback, 输出反馈模式) 是对称加密的一种工作模式。它的最大特点是将 SM4 这种"块加密"引擎当作"伪随机数生成器"来使用。
1. 工作原理
在 OFB 模式下,SM4 不直接加密明文,而是加密上一次加密输出的反馈向量:
- 初始化:选取一个初始化向量(IV)。
- 生成密钥流:用 SM4 加密 IV,得到第一个密钥块。
- 反馈:将这个密钥块再输入 SM4 加密,得到第二个密钥块,以此类推。
- 加密明文:将生成的密钥流与明文进行 异或 (XOR) 运算。
2. 核心公式
若以 Ek 表示使用密钥 k 的 SM4 加密函数,Pi 为明文块,Ci 为密文块,Oi 为输出块:

SM4-OFB 的技术优势
- 无填充要求:由于明文是直接与密钥流异或,明文长度不需要是 16 字节的整数倍。这在处理流媒体或字节流数据时非常方便。
- 错误不蔓延:如果密文在传输中某一位损坏,只会影响对应的那一位明文,不会像 CBC 模式那样导致整个后续解密失败。
- 预计算能力 :因为密钥流的生成完全不依赖明文,系统可以提前算出大量的密钥流块,等到明文一到,直接进行异或运算,极大地降低了加密延迟。
- 解密与加密对称 :解密时,依然是使用 SM4 的加密流程来生成密钥流,然后再异或。这意味着硬件实现时只需要加密电路。
安全注意事项
尽管 SM4-OFB 非常高效,但在实际工程中必须遵守以下准则:
1. IV 的唯一性(绝对禁止重用)
这是 OFB 模式的致命伤。如果使用相同的密钥和相同的 IV 加密两段不同的明文,则:

攻击者可以通过异或结果推导出明文内容。IV 必须是不可预测且唯一的。
2. 认证缺失
OFB 模式本身不提供完整性保护 。攻击者虽然不知道明文,但可以通过翻转密文中的特定位(Bit-flipping attack)来精确修改解密后的明文。在安全要求高的场景,建议配合 HMAC 使用。
示例(c++)
c++
#include <iostream>
#include <vector>
#include <cstring>
#include <gmssl/sm4.h>
/**
* SM4-OFB 加解密实现函数
* @param key 16字节密钥
* @param iv 16字节初始化向量 (每个消息必须唯一)
* @param input 输入数据指针
* @param input_len 输入数据长度
* @param output 输出缓冲区指针 (长度应等于 input_len)
*/
void sm4_ofb_transform(const uint8_t key[16],
const uint8_t iv[16],
const uint8_t* input,
size_t input_len,
uint8_t* output) {
SM4_KEY sm4_key;
uint8_t iv_buffer[16];
uint8_t block_out[16];
int offset = 0;
// 1. 初始化 SM4 密钥
sm4_set_encrypt_key(&sm4_key, key);
// 2. 拷贝 IV,因为 OFB 需要不断更新反馈向量
memcpy(iv_buffer, iv, 16);
// 3. 逐块处理
for (size_t i = 0; i < input_len; i++) {
// 当处理到块的边界(或起始)时,生成新的密钥流块
if (offset == 0) {
sm4_encrypt(&sm4_key, iv_buffer, block_out);
// 将加密后的输出作为下一次加密的输入 (Feedback)
memcpy(iv_buffer, block_out, 16);
}
// 4. 将密钥流与明文/密文异或
output[i] = input[i] ^ block_out[offset];
// 更新块内偏移,SM4 块大小为 16 字节
offset = (offset + 1) % 16;
}
}
// 使用示例
int main() {
uint8_t key[16] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10};
uint8_t iv[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
const char* text = "Hello, SM4-OFB Security!";
size_t len = strlen(text);
std::vector<uint8_t> encrypted(len);
std::vector<uint8_t> decrypted(len);
// 加密
sm4_ofb_transform(key, iv, (uint8_t*)text, len, encrypted.data());
// 解密 (OFB 模式解密函数与加密完全一致,IV 需相同)
sm4_ofb_transform(key, iv, encrypted.data(), len, decrypted.data());
std::cout << "Original: " << text << std::endl;
std::cout << "Decrypted: " << std::string(decrypted.begin(), decrypted.end()) << std::endl;
return 0;
}
总结
| 特性 | SM4-ECB (基本模式) | SM4-CBC (链式模式) | SM4-OFB (反馈模式) |
|---|---|---|---|
| 安全性 | 低(易受模式分析) | 高 | 高(IV 唯一时) |
| 填充 | 需要 | 需要 | 不需要 |
| 并行计算 | 支持 | 不支持解密外 | 支持预计算 |
| 错误影响 | 仅本块 | 蔓延至下一块 | 无蔓延 |