一、简介
国密算法是由国家密码管理局认定的自主可控国产密码算法体系,包含SM系列及祖冲之(ZUC)算法等核心技术。该体系涵盖对称加密(SM1、SM4、SM7)、非对称加密(SM2、SM9)、哈希算法(SM3)与流密码(ZUC)等类型,形成完整的密码技术生态。
二、核心组件
轮函数结构
SM4的轮函数设计结合了多种运算:
· 异或运算(模2加)
· 循环左移
· 非线性字节变换(S盒)
S盒变换
S盒是8位输入、8位输出的非线性置换。查询规则是:以输入的高4位为行号,低4位为列号,查找对应值。例如,输入 0xEF,查询S盒第E行、第F列得到输出值 0x84。
密钥扩展算法
SM4的子密钥生成算法与加密算法结构类似,通过初始密钥生成32个轮密钥。
三、加密与解密过程
加密
SM4采用对合运算设计,即解密算法与加密算法相同,这大大简化了实现。它的加密过程如下:

- 分组:128位明文分为4个32位的字。
- 经过32轮加密变换。每一轮的加密变换为:
(a)循环左移
(b)最后一个字经过轮函数F得到
(c)32轮的加密变换结束后,将4个字反序变化后,得到128位密文。
- SM4每轮结构
上一轮的数据,i=1,2…32。循环左移32位后经过加密函数(轮函数),得左移后最后32位数据,此轮输出作为下一轮迭代的输入,迭代32轮后做反序变换,得到密文。
解密

四、工作模式
1、ECB模式(电子密码本模式)
- 原理:每个明文块独立加密,相同明文块生成相同密文块。
- 优缺点:实现简单、可并行处理,但安全性较低,可能泄露数据模式。
- 加密模版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| def sm4_ecb_encrypt(plaintext, key): """ SM4 ECB模式加密 :param plaintext: 明文(字节串) :param key: 128位密钥(16字节) :return: 密文(字节串) """ if len(plaintext) % 16 != 0: plaintext = pkcs7_padding(plaintext) blocks = [plaintext[i:i+16] for i in range(0, len(plaintext), 16)] cipher_blocks = [] for block in blocks: cipher_block = sm4_encrypt_block(block, key) cipher_blocks.append(cipher_block) return b''.join(cipher_blocks)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| def sm4_ecb_decrypt(ciphertext, key): """ SM4 ECB模式解密 :param ciphertext: 密文(字节串) :param key: 128位密钥(16字节) :return: 解密后的明文(字节串) """ if len(ciphertext) % 16 != 0: raise ValueError("密文长度必须是16字节的倍数") blocks = [ciphertext[i:i+16] for i in range(0, len(ciphertext), 16)] plain_blocks = [] for block in blocks: plain_block = sm4_decrypt_block(block, key) plain_blocks.append(plain_block) decrypted_data = b''.join(plain_blocks) return pkcs7_unpadding(decrypted_data)
|
gmssl库解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT import os
def sm4_ecb_decrypt_gmssl(ciphertext, key): """ 使用gmssl库进行SM4 ECB模式解密 :param ciphertext: 密文(字节串) :param key: 128位密钥(16字节) :return: 解密后的明文(字节串) """ crypt_sm4 = CryptSM4() crypt_sm4.set_key(key, SM4_DECRYPT) plaintext = crypt_sm4.crypt_ecb(ciphertext) return plaintext
def sm4_ecb_encrypt_gmssl(plaintext, key): """ 对应的ECB加密函数 """ crypt_sm4 = CryptSM4() crypt_sm4.set_key(key, SM4_ENCRYPT) ciphertext = crypt_sm4.crypt_ecb(plaintext) return ciphertext
|
2、CBC模式(密码分组链接模式)
- 原理:每个明文块与前一个密文块异或后再加密。首个块使用初始化向量IV。
- 优缺点:安全性高于ECB,相同明文块加密结果不同;但加密过程无法并行。
- 加密模版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| def sm4_cbc_encrypt(plaintext, key, iv): """ SM4 CBC模式加密 :param plaintext: 明文(字节串) :param key: 128位密钥(16字节) :param iv: 初始化向量(16字节) :return: 密文(字节串) """ plaintext = pkcs7_padding(plaintext) blocks = [plaintext[i:i+16] for i in range(0, len(plaintext), 16)] cipher_blocks = [] prev_block = iv for block in blocks: xored_block = bytes(a ^ b for a, b in zip(block, prev_block)) cipher_block = sm4_encrypt_block(xored_block, key) cipher_blocks.append(cipher_block) prev_block = cipher_block return b''.join(cipher_blocks)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| def sm4_cbc_decrypt(ciphertext, key, iv): """ SM4 CBC模式解密 :param ciphertext: 密文(字节串) :param key: 128位密钥(16字节) :param iv: 初始化向量(16字节) :return: 解密后的明文(字节串) """ if len(ciphertext) % 16 != 0: raise ValueError("密文长度必须是16字节的倍数") blocks = [ciphertext[i:i+16] for i in range(0, len(ciphertext), 16)] plain_blocks = [] prev_block = iv for block in blocks: decrypted_block = sm4_decrypt_block(block, key) plain_block = bytes(a ^ b for a, b in zip(decrypted_block, prev_block)) plain_blocks.append(plain_block) prev_block = block decrypted_data = b''.join(plain_blocks) return pkcs7_unpadding(decrypted_data)
|
gmssl库解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| def sm4_cbc_decrypt_gmssl(ciphertext, key, iv): """ 使用gmssl库进行SM4 CBC模式解密 :param ciphertext: 密文(字节串) :param key: 128位密钥(16字节) :param iv: 初始化向量(16字节) :return: 解密后的明文(字节串) """ crypt_sm4 = CryptSM4() crypt_sm4.set_key(key, SM4_DECRYPT) plaintext = crypt_sm4.crypt_cbc(iv, ciphertext) return plaintext
def sm4_cbc_encrypt_gmssl(plaintext, key, iv): """ 对应的CBC加密函数 """ crypt_sm4 = CryptSM4() crypt_sm4.set_key(key, SM4_ENCRYPT) ciphertext = crypt_sm4.crypt_cbc(iv, plaintext) return ciphertext
|
3、CTR模式(计数器模式)
- 原理:将分组密码转换为流密码。通过加密计数器值生成密钥流,与明文异或得到密文。
- 优缺点:加解密可并行,适合高性能场景;无需填充。
- 模版(CTR模式的解密与加密完全相同,dectypt encrypt)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| def sm4_ctr_encrypt(plaintext, key, nonce): """ SM4 CTR模式加密(也是解密) :param plaintext: 明文/密文(字节串) :param key: 128位密钥(16字节) :param nonce: 随机数(8字节) :return: 密文/明文(字节串) """ ctr = 0 ciphertext = bytearray() for i in range(0, len(plaintext), 16): counter_block = nonce + ctr.to_bytes(8, 'big') keystream_block = sm4_encrypt_block(counter_block, key) block = plaintext[i:i+16] encrypted_block = bytes(a ^ b for a, b in zip(block, keystream_block[:len(block)])) ciphertext.extend(encrypted_block) ctr += 1 return bytes(ciphertext)
|
pycryptodome库解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| from Crypto.Cipher import SM4 from Crypto.Util import Counter
def sm4_ctr_decrypt_pycryptodome(ciphertext, key, nonce): """ 使用pycryptodome库进行SM4 CTR模式解密 :param ciphertext: 密文(字节串) :param key: 128位密钥(16字节) :param nonce: 随机数(8字节) :return: 解密后的明文(字节串) """ ctr = Counter.new(64, prefix=nonce, initial_value=0) cipher = SM4.new(key, SM4.MODE_CTR, counter=ctr) plaintext = cipher.decrypt(ciphertext) return plaintext
def sm4_ctr_encrypt_pycryptodome(plaintext, key, nonce): """ CTR模式加密函数(与解密相同) """ ctr = Counter.new(64, prefix=nonce, initial_value=0) cipher = SM4.new(key, SM4.MODE_CTR, counter=ctr) ciphertext = cipher.encrypt(plaintext) return ciphertext
|
gmssl替代方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def sm4_ctr_gmssl(plaintext, key, iv): """ gmssl的CTR模式(实际上叫"OFB"模式但行为类似CTR) 注意:gmssl的CTR实现与标准略有不同 """ crypt_sm4 = CryptSM4() crypt_sm4.set_key(key, SM4_ENCRYPT) result = crypt_sm4.crypt_ctr(iv, plaintext) return result
sm4_ctr_decrypt_gmssl = sm4_ctr_gmssl sm4_ctr_encrypt_gmssl = sm4_ctr_gmssl
|
4、辅助函数实现
PKCS7填充函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| def pkcs7_padding(data): """PKCS7填充""" padding_length = 16 - (len(data) % 16) padding = bytes([padding_length] * padding_length) return data + padding
def pkcs7_unpadding(data): """PKCS7去除填充""" if len(data) % 16 != 0 or len(data) == 0: raise ValueError("无效的填充数据") padding_length = data[-1] if padding_length < 1 or padding_length > 16: raise ValueError("无效的填充长度") if data[-padding_length:] != bytes([padding_length] * padding_length): raise ValueError("无效的填充数据") return data[:-padding_length]
|
5、总结
- ECB模式:简单但不安全,每个块独立加密
- CBC模式:更安全,需要IV,加解密过程不同
- CTR模式:流密码模式,加解密相同,无需填充
- IV/Nonce:CBC需要16字节IV,CTR需要8字节Nonce
- 填充:ECB和CBC需要填充,CTR不需要
五、注意
- IV(初始化向量)管理:CBC、CTR等模式需要IV,它应当随机生成且无需保密,但每次加密应使用不同的IV。
- 密钥安全:密钥需要安全存储和管理,避免硬编码在代码中。
- 认证加密:基本加密模式只提供机密性,如需同时验证完整性,可考虑SM4-GCM或专用认证加密算法如SMRAE。
- 性能优化:可通过查找表、并行处理等方式优化SM4实现。