国密算法是国家密码局认定的国产商用密码算法,SM2、SM3、SM4 是常用公开算法,具体介绍如下:
- SM2(非对称加密算法 )
- 原理与特性:基于椭圆曲线密码学(ECC),利用椭圆曲线上点群离散对数难题保障安全。256 位密钥的安全强度,已高于 2048 位的 RSA 算法 ,且签名、密钥生成速度更快,还在一定程度上可抗量子计算攻击。
- 功能与应用:涵盖椭圆曲线数字签名、密钥交换协议、公钥加密算法,用于数字签名、密钥协商、数据加密。像网络通信中身份认证、电子政务 / 商务里重要文件签署与加密传输,国密 SSL 证书也会用其做密钥交换和数字签名 。
- SM3(哈希算法 / 杂凑算法 )
- 原理与特性:自主设计的密码杂凑算法,输出 256 比特哈希值,抗碰撞能力强(不同数据难产生相同哈希 ),安全性高于 MD5(128 比特输出 )、SHA - 1(160 比特输出 )。
- 功能与应用:用于数字签名验证、消息认证码生成 / 验证、随机数生成,保障数据完整性。比如数字证书、电子支付里的数据完整性校验,区块链生成区块哈希也会用到 。
- SM4(对称加密算法 )
- 原理与特性:分组对称密码算法,密钥长度和分组长度均为 128 位,采用特定 S 盒和线性变换矩阵,安全性高,计算高效,与 AES 算法密钥长度等相当,优于 3DES 算法。
- 功能与应用:实现数据加 / 解密,保证机密性。可用于 SSL/TLS 协议加密传输数据,也能加密存储设备上的敏感数据,像企业内部系统数据加密、物联网设备通信加密 。
下面,我们通过python来实现国密算法(SM2、SM3、SM4)的示例代码:
import secrets
import hashlib
import math
from typing import Tuple, List
# SM3 哈希算法实现
class SM3:
def __init__(self):
self.block_size = 64
self.digest_size = 32
self.IV = [
0x7380166F, 0x4914B2B9, 0x172442D7, 0xDA8A0600,
0xA96F30BC, 0x163138AA, 0xE38DEE4D, 0xB0FB0E4E
]
def rotate_left(self, x: int, n: int) -> int:
return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF
def ff(self, x: int, y: int, z: int, j: int) -> int:
if 0 <= j <= 15:
return x ^ y ^ z
else:
return (x & y) | (x & z) | (y & z)
def gg(self, x: int, y: int, z: int, j: int) -> int:
if 0 <= j <= 15:
return x ^ y ^ z
else:
return (x & y) | (~x & z)
def p0(self, x: int) -> int:
return x ^ self.rotate_left(x, 9) ^ self.rotate_left(x, 17)
def p1(self, x: int) -> int:
return x ^ self.rotate_left(x, 15) ^ self.rotate_left(x, 23)
def padding(self, message: bytes) -> bytes:
ml = len(message) * 8
message += b'\x80'
while (len(message) * 8) % 512 != 448:
message += b'\x00'
message += ml.to_bytes(8, 'big')
return message
def block_process(self, block: bytes, v: List[int]) -> List[int]:
w = []
for i in range(16):
w.append(int.from_bytes(block[i*4:(i+1)*4], 'big'))
for i in range(16, 68):
w.append(self.p1(w[i-16] ^ w[i-9] ^ self.rotate_left(w[i-3], 15)) ^
self.rotate_left(w[i-13], 7) ^ w[i-6])
w1 = []
for i in range(64):
w1.append(w[i] ^ w[i+4])
a, b, c, d, e, f, g, h = v
for j in range(64):
if j <= 15:
t_j = 0x79CC4519
else:
t_j = 0x7A879D8A
ss1 = self.rotate_left(
(self.rotate_left(a, 12) + e + self.rotate_left(t_j, j % 32)) & 0xFFFFFFFF, 7
)
ss2 = ss1 ^ self.rotate_left(a, 12)
tt1 = (self.ff(a, b, c, j) + d + ss2 + w1[j]) & 0xFFFFFFFF
tt2 = (self.gg(e, f, g, j) + h + ss1 + w[j]) & 0xFFFFFFFF
d = c
c = self.rotate_left(b, 9)
b = a
a = tt1
h = g
g = self.rotate_left(f, 19)
f = e
e = self.p0(tt2)
return [
(v[0] ^ a) & 0xFFFFFFFF, (v[1] ^ b) & 0xFFFFFFFF,
(v[2] ^ c) & 0xFFFFFFFF, (v[3] ^ d) & 0xFFFFFFFF,
(v[4] ^ e) & 0xFFFFFFFF, (v[5] ^ f) & 0xFFFFFFFF,
(v[6] ^ g) & 0xFFFFFFFF, (v[7] ^ h) & 0xFFFFFFFF
]
def digest(self, message: bytes) -> bytes:
padded_msg = self.padding(message)
blocks = [padded_msg[i:i+64] for i in range(0, len(padded_msg), 64)]
v = self.IV.copy()
for block in blocks:
v = self.block_process(block, v)
digest = b''
for num in v:
digest += num.to_bytes(4, 'big')
return digest
def hexdigest(self, message: bytes) -> str:
return self.digest(message).hex()
# SM2 椭圆曲线密码算法实现
class SM2:
def __init__(self):
# SM2 曲线参数
self.p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
self.a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
self.b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940
self.n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
self.Gx = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C7
self.Gy = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F
# 基点 G
self.G = (self.Gx, self.Gy)
def mod_inverse(self, a: int, p: int) -> int:
if a == 0:
return 0
lm, hm = 1, 0
low, high = a % p, p
while low > 1:
r = high // low
nm, new = hm - lm * r, high - low * r
lm, low, hm, high = nm, new, lm, low
return lm % p
def point_add(self, P: Tuple[int, int], Q: Tuple[int, int]) -> Tuple[int, int]:
if P is None:
return Q
if Q is None:
return P
x1, y1 = P
x2, y2 = Q
if x1 == x2 and y1 != y2:
return None
if P == Q:
s = ((3 * x1 * x1 + self.a) * self.mod_inverse(2 * y1, self.p)) % self.p
else:
s = ((y2 - y1) * self.mod_inverse(x2 - x1, self.p)) % self.p
x3 = (s * s - x1 - x2) % self.p
y3 = (s * (x1 - x3) - y1) % self.p
return (x3, y3)
def point_mul(self, k: int, P: Tuple[int, int]) -> Tuple[int, int]:
result = None
current = P
while k:
if k & 1:
result = self.point_add(result, current)
current = self.point_add(current, current)
k >>= 1
return result
def generate_key_pair(self) -> Tuple[int, Tuple[int, int]]:
private_key = secrets.randbelow(self.n - 1) + 1
public_key = self.point_mul(private_key, self.G)
return private_key, public_key
def encrypt(self, public_key: Tuple[int, int], message: bytes) -> bytes:
# 简化版加密,实际应用需要更复杂的处理
k = secrets.randbelow(self.n - 1) + 1
C1 = self.point_mul(k, self.G)
x2, y2 = self.point_mul(k, public_key)
# 使用SM3进行密钥派生
sm3 = SM3()
t = sm3.hexdigest(f"{x2}{y2}".encode())
# 消息加密
message_hex = message.hex()
encrypted_hex = hex(int(message_hex, 16) ^ int(t, 16))[2:].zfill(len(message_hex))
# 计算SM3哈希
sm3_hash = sm3.hexdigest(f"{x2}{message}{y2}".encode())
return f"{C1[0]:x}{C1[1]:x}{encrypted_hex}{sm3_hash}".encode()
def decrypt(self, private_key: int, ciphertext: bytes) -> bytes:
# 简化版解密,实际应用需要更复杂的处理
ciphertext_str = ciphertext.decode()
len_c1 = 64 # 假设C1占64个字符
x1 = int(ciphertext_str[:32], 16)
y1 = int(ciphertext_str[32:64], 16)
C1 = (x1, y1)
x2, y2 = self.point_mul(private_key, C1)
# 使用SM3进行密钥派生
sm3 = SM3()
t = sm3.hexdigest(f"{x2}{y2}".encode())
# 密文长度
len_encrypted = len(ciphertext_str) - len_c1 - 64
encrypted_hex = ciphertext_str[len_c1:len_c1 + len_encrypted]
# 解密
message_hex = hex(int(encrypted_hex, 16) ^ int(t, 16))[2:].zfill(len(encrypted_hex))
message = bytes.fromhex(message_hex)
# 验证哈希
sm3_hash = ciphertext_str[-64:]
computed_hash = sm3.hexdigest(f"{x2}{message}{y2}".encode())
if sm3_hash != computed_hash:
raise ValueError("解密失败:哈希验证不匹配")
return message
# SM4 分组密码算法实现
class SM4:
def __init__(self):
self.block_size = 16
self.key_size = 16
self.rounds = 32
# 系统参数 FK
self.FK = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC]
# 固定参数 CK
self.CK = []
for i in range(32):
idx = i * 4
ck = 0
for j in range(4):
t = (4 * i + j) % 256
ck |= t << (24 - 8 * j)
self.CK.append(ck)
# S盒
self.S_BOX = [
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
]
def rotate_left(self, x: int, n: int) -> int:
return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF
def byte_sub(self, a: int) -> int:
return self.S_BOX[a]
def l_t(self, a: int) -> int:
b = 0
for i in range(4):
b |= self.byte_sub((a >> (24 - 8 * i)) & 0xFF) << (24 - 8 * i)
c = b ^ self.rotate_left(b, 2) ^ self.rotate_left(b, 10) ^ self.rotate_left(b, 18) ^ self.rotate_left(b, 24)
return c
def l1_t(self, a: int) -> int:
b = 0
for i in range(4):
b |= self.byte_sub((a >> (24 - 8 * i)) & 0xFF) << (24 - 8 * i)
c = b ^ self.rotate_left(b, 13) ^ self.rotate_left(b, 23)
return c
def key_expansion(self, key: bytes) -> List[int]:
MK = [
(key[0] << 24) | (key[1] << 16) | (key[2] << 8) | key[3],
(key[4] << 24) | (key[5] << 16) | (key[6] << 8) | key[7],
(key[8] << 24) | (key[9] << 16) | (key[10] << 8) | key[11],
(key[12] << 24) | (key[13] << 16) | (key[14] << 8) | key[15]
]
K = [MK[i] ^ self.FK[i] for i in range(4)]
rk = []
for i in range(32):
tmp = K[i+1] ^ K[i+2] ^ K[i+3] ^ self.CK[i]
rk.append(self.l1_t(tmp))
K.append(rk[i])
return rk
def encrypt_block(self, block: bytes, key: bytes) -> bytes:
rk = self.key_expansion(key)
X = [
(block[0] << 24) | (block[1] << 16) | (block[2] << 8) | block[3],
(block[4] << 24) | (block[5] << 16) | (block[6] << 8) | block[7],
(block[8] << 24) | (block[9] << 16) | (block[10] << 8) | block[11],
(block[12] << 24) | (block[13] << 16) | (block[14] << 8) | block[15]
]
for i in range(32):
tmp = X[i+1] ^ X[i+2] ^ X[i+3] ^ rk[i]
X.append(X[i] ^ self.l_t(tmp))
# 反序输出
ciphertext = []
for i in range(3, -1, -1):
word = X[i+32]
for j in range(4):
ciphertext.append((word >> (24 - 8 * j)) & 0xFF)
return bytes(ciphertext)
def decrypt_block(self, block: bytes, key: bytes) -> bytes:
# SM4加密和解密算法相同,只是轮密钥使用顺序相反
rk = self.key_expansion(key)[::-1]
return self.encrypt_block(block, bytes.fromhex(''.join([f"{k:08x}" for k in rk])))
def encrypt(self, plaintext: bytes, key: bytes) -> bytes:
# 简单ECB模式加密,实际应用中应使用更安全的模式
if len(plaintext) % self.block_size != 0:
# 填充到块大小的整数倍
padding_length = self.block_size - (len(plaintext) % self.block_size)
plaintext += bytes([padding_length]) * padding_length
ciphertext = b''
for i in range(0, len(plaintext), self.block_size):
block = plaintext[i:i+self.block_size]
ciphertext += self.encrypt_block(block, key)
return ciphertext
def decrypt(self, ciphertext: bytes, key: bytes) -> bytes:
if len(ciphertext) % self.block_size != 0:
raise ValueError("密文长度必须是块大小的整数倍")
plaintext = b''
for i in range(0, len(ciphertext), self.block_size):
block = ciphertext[i:i+self.block_size]
plaintext += self.decrypt_block(block, key)
# 去除填充
padding_length = plaintext[-1]
if padding_length > self.block_size:
raise ValueError("无效的填充")
return plaintext[:-padding_length]
# 示例使用
if __name__ == "__main__":
# SM3示例
sm3 = SM3()
message = b"Hello, SM3!"
digest = sm3.hexdigest(message)
print(f"SM3 digest: {digest}")
# SM2示例
sm2 = SM2()
private_key, public_key = sm2.generate_key_pair()
message = b"Hello, SM2!"
encrypted = sm2.encrypt(public_key, message)
decrypted = sm2.decrypt(private_key, encrypted)
print(f"SM2 decrypted: {decrypted}")
# SM4示例
sm4 = SM4()
key = b"0123456789abcdef" # 16字节密钥
plaintext = b"Hello, SM4!"
# 填充到16字节倍数
if len(plaintext) % 16 != 0:
plaintext += b'\x00' * (16 - len(plaintext) % 16)
encrypted = sm4.encrypt(plaintext, key)
decrypted = sm4.decrypt(encrypted, key)
print(f"SM4 decrypted: {decrypted}")
注意:以上仅为演示,实际生产环境中建议使用经过严格安全审计的密码学库,如 gmssl 等。
下面是一个基于gmssl
库实现 SM4 算法的示例代码,包含 ECB 和 CBC 两种模式的加密解密:
from gmssl import sm4
class SM4Cipher:
def __init__(self):
"""初始化SM4加密器"""
self.sm4_crypt = sm4.CryptSM4()
def encrypt_ecb(self, key: bytes, plaintext: bytes) -> bytes:
"""
使用ECB模式进行SM4加密
参数:
key: 加密密钥,必须为16字节
plaintext: 待加密的明文
返回:
加密后的密文
"""
# 检查密钥长度
if len(key) != 16:
raise ValueError("SM4密钥长度必须为16字节")
# 填充明文到16字节的倍数
padded_plaintext = self._pkcs7_padding(plaintext)
# 设置密钥和模式
self.sm4_crypt.set_key(key, sm4.SM4_ENCRYPT)
# 加密
ciphertext = self.sm4_crypt.crypt_ecb(padded_plaintext)
return ciphertext
def decrypt_ecb(self, key: bytes, ciphertext: bytes) -> bytes:
"""
使用ECB模式进行SM4解密
参数:
key: 解密密钥,必须为16字节
ciphertext: 待解密的密文
返回:
解密后的明文
"""
# 检查密钥长度
if len(key) != 16:
raise ValueError("SM4密钥长度必须为16字节")
# 设置密钥和模式
self.sm4_crypt.set_key(key, sm4.SM4_DECRYPT)
# 解密
padded_plaintext = self.sm4_crypt.crypt_ecb(ciphertext)
# 去除填充
plaintext = self._pkcs7_unpadding(padded_plaintext)
return plaintext
def encrypt_cbc(self, key: bytes, iv: bytes, plaintext: bytes) -> bytes:
"""
使用CBC模式进行SM4加密
参数:
key: 加密密钥,必须为16字节
iv: 初始化向量,必须为16字节
plaintext: 待加密的明文
返回:
加密后的密文
"""
# 检查密钥和IV长度
if len(key) != 16:
raise ValueError("SM4密钥长度必须为16字节")
if len(iv) != 16:
raise ValueError("SM4 CBC模式IV长度必须为16字节")
# 填充明文到16字节的倍数
padded_plaintext = self._pkcs7_padding(plaintext)
# 设置密钥和模式
self.sm4_crypt.set_key(key, sm4.SM4_ENCRYPT)
# 加密
ciphertext = self.sm4_crypt.crypt_cbc(iv, padded_plaintext)
return ciphertext
def decrypt_cbc(self, key: bytes, iv: bytes, ciphertext: bytes) -> bytes:
"""
使用CBC模式进行SM4解密
参数:
key: 解密密钥,必须为16字节
iv: 初始化向量,必须为16字节
ciphertext: 待解密的密文
返回:
解密后的明文
"""
# 检查密钥和IV长度
if len(key) != 16:
raise ValueError("SM4密钥长度必须为16字节")
if len(iv) != 16:
raise ValueError("SM4 CBC模式IV长度必须为16字节")
# 设置密钥和模式
self.sm4_crypt.set_key(key, sm4.SM4_DECRYPT)
# 解密
padded_plaintext = self.sm4_crypt.crypt_cbc(iv, ciphertext)
# 去除填充
plaintext = self._pkcs7_unpadding(padded_plaintext)
return plaintext
def _pkcs7_padding(self, data: bytes) -> bytes:
"""使用PKCS#7标准进行填充"""
block_size = 16
padding_length = block_size - (len(data) % block_size)
padding = bytes([padding_length]) * padding_length
return data + padding
def _pkcs7_unpadding(self, padded_data: bytes) -> bytes:
"""去除PKCS#7填充"""
padding_length = padded_data[-1]
if padding_length < 1 or padding_length > 16:
raise ValueError("无效的填充")
if padded_data[-padding_length:] != bytes([padding_length]) * padding_length:
raise ValueError("无效的填充")
return padded_data[:-padding_length]
# 示例使用
if __name__ == "__main__":
# 创建SM4加密器实例
cipher = SM4Cipher()
# 示例密钥和IV(必须为16字节)
key = b'0123456789abcdef'
iv = b'abcdef9876543210'
# 待加密的明文
plaintext = b'Hello, SM4 encryption using CBC mode!'
print(f"原始明文: {plaintext}")
# 使用CBC模式加密
ciphertext = cipher.encrypt_cbc(key, iv, plaintext)
print(f"CBC模式密文: {ciphertext.hex()}")
# 使用CBC模式解密
decrypted_text = cipher.decrypt_cbc(key, iv, ciphertext)
print(f"CBC模式解密后: {decrypted_text}")
# 使用ECB模式加密(注意:ECB模式不安全,仅作演示)
ciphertext_ecb = cipher.encrypt_ecb(key, plaintext)
print(f"ECB模式密文: {ciphertext_ecb.hex()}")
# 使用ECB模式解密
decrypted_text_ecb = cipher.decrypt_ecb(key, ciphertext_ecb)
print(f"ECB模式解密后: {decrypted_text_ecb}")
注意事项:
- ECB 模式不安全,不推荐在实际应用中使用
- 运行此代码前需要安装 gmssl 库:
pip install gmssl
- 密钥和 IV 在实际应用中应安全生成和存储
- 实际应用中建议使用更安全的加密模式如 CBC、CTR 或 GCM