说明
SM4和AES只是加密算法不同,使用起来几乎没有区别,AES相关的例程可以参考:
基于mbedtls的AES加密(C/C++)
基于OpenSSL的AES加密(C/C++)
本文主要介绍SM4加密算法,并提供库里没有的CTR模式模式
加密模式介绍
ECB模式(Electronic Codebook):
特点:
- 独立性: 每个明文块都独立地使用相同的密钥进行加密,因此加密过程是相互独立的。
- 并行性: 由于每个块之间没有依赖关系,ECB模式具有较好的并行性,可以同时加密多个块。
加密过程:
-
将明文划分为固定大小的块(例如128位)。
-
对每个块独立使用AES加密算法,使用相同的密钥。
-
输出得到相应的密文块。
CBC模式(Cipher Block Chaining):
特点:
- 链接性: 每个明文块在加密之前都与前一个密文块进行异或运算,建立起块之间的链接,增加了安全性。
- 初始块: 使用初始化向量(IV)引导第一个块的加密,避免了相同的明文块生成相同的密文块。
加密过程:
-
使用初始化向量(IV)与第一个明文块进行异或运算。
-
对异或的结果使用AES加密算法,得到第一个密文块。
-
将第一个密文块作为下一个明文块的IV,继续进行异或和加密。
-
重复这个过程直到加密完所有的块。
CTR模式(Counter):
特点:
- 并行性: 由于每个块都被视为一个计数器的值,不需要保持块之间的状态,因此CTR模式具有良好的并行性。
- 计数器: 每个块都使用一个唯一的计数器值,避免了相同的明文块生成相同的密文块。
加密过程:
- 生成一个初始计数器值(Counter)和初始化向量(IV)。
- 将计数器值与IV组合作为输入,使用AES加密算法生成伪随机流。
- 将伪随机流与明文块进行异或运算,得到密文块。
- 更新计数器值,继续生成伪随机流,重复以上步骤直到加密完所有的块。
AES和SM4的区别
设计和标准制定
- AES: 由美国国家标准技术研究所(NIST)于2001年制定,是国际上广泛认可的加密标准。
- SM4: 由中国国家密码管理局在2006年制定,主要在中国国内使用。
密钥长度
- AES: 支持128位、192位和256位的密钥长度,分别称为AES-128、AES-192和AES-256。
- SM4: 使用固定的128位密钥。
分组长度
- AES: 使用128位的分组长度。
- SM4: 同样使用128位的分组长度。
轮数
- AES: 轮数取决于密钥长度,分别为10轮(AES-128)、12轮(AES-192)和14轮(AES-256)。
- SM4: 固定为32轮。
S盒(Substitution Box)
- AES: 使用固定的S盒,是一个预定义的字节替换表。
- SM4: 使用非线性的S盒,通过运算生成。
应用领域
- AES: 作为国际上广泛应用的加密算法,适用于各种领域的安全通信和数据保护。
- SM4: 主要在中国国内使用,用于信息安全领域。
国际认可
- AES: 经过全球范围内的广泛评估,得到广泛认可。
- SM4: 目前主要在中国国内应用,国际认可度相对较低。
通过对比这两种算法的设计、应用和性能特征,可以更好地理解它们在不同背景下的使用场景和优势。在选择使用哪种算法时,应考虑特定的安全需求和国际标准。
代码
输出
ECB ENCYRPT(hex): 9a991f25e3303e12173f04f698ed5f4d
ECB DECYRPT(str): 1234567890abcdef
CBC ENCYRPT(hex): d2d68ed9fe06cb40c9a150aa5917f15fa80538810c8117273f53ac7a0735b8f5
CBC DECYRPT(str): 1234567890abcdef123456789abcdef0
CRT ENCYRPT(hex): abab2c11d606092a2e0f6594fb893a2b7aa45f0c5b207da83e4bb3eb754c1f00
CRT DECYRPT(str): 1234567890abcdef123456789abcdef0
源代码
from pysm4 import encrypt_ecb, decrypt_ecb, encrypt_cbc, decrypt_cbc
import base64sm4_key = "1234567890123456";
sm4_iv = "1234567890abcdef";
sm4_in = "1234567890abcdef123456789abcdef0";def SM4_CTR(data, key, iv, byteorder = "little"):'''入参和返回值均为bytes类型'''i = 0ret = []groups_num = len(data) // 16# bytes 转 intif byteorder == "little":iv_int = int.from_bytes(iv[0:4], byteorder)else:iv_int = int.from_bytes(iv[12:16], byteorder)for i in range(groups_num):temp_bs64 = encrypt_ecb(iv, key)temp_bytes = base64.b64decode(temp_bs64)for j in range(16):ret.append(data[i*16+j] ^ temp_bytes[j])iv_int += 1if byteorder == "little":iv = iv_int.to_bytes(4, byteorder) + iv[4:]else:iv = iv[:12] + iv_int.to_bytes(4, byteorder)if (len(data) % 16):temp_bs64 = encrypt_ecb(iv, key)temp_bytes = base64.b64decode(temp_bs64)for j in range(len(data) % 16):ret.append(data[i*16+j] ^ temp_bytes[j])return retdef ecb_test():ret = encrypt_ecb(sm4_in.encode(), sm4_key.encode())print("ECB ENCYRPT(hex): ", base64.b64decode(ret).hex()[:32])ret = decrypt_ecb(ret, sm4_key.encode())print("ECB DECYRPT(str): ", ret[:16])def ccb_test():ret = encrypt_cbc(sm4_in.encode(), sm4_key.encode(), sm4_iv.encode())print("CBC ENCYRPT(hex): ", base64.b64decode(ret).hex()[:64])ret = decrypt_cbc(ret, sm4_key.encode(), sm4_iv.encode())print("CBC DECYRPT(str): ", ret[:32])def ctr_test():ret = SM4_CTR(sm4_in.encode(), sm4_key.encode(), sm4_iv.encode(), "big")print("CRT ENCYRPT(hex): ", bytes(ret).hex())ret = SM4_CTR(bytes(ret), sm4_key.encode(), sm4_iv.encode(), "big")print("CRT DECYRPT(str): ", bytes(ret).decode())if __name__ == '__main__':ecb_test()ccb_test()ctr_test()