目录
一.算法简介
事实上从 2010 年开始,我国国家密码管理局就已经开始陆续发布了一系列国产加密算法,这其中就包括 SM1、SM2、SM3 、SM4、SM7、SM9、ZUC(祖冲之加密算法)等,SM 代表商密,即商业密码,是指用于商业的、不涉及国家秘密的密码技术。SM1 和 SM7的算法不公开,其余算法都已成为ISO/IEC 国际标准。
在这些国产加密算法中,SM2、SM3、SM4 三种加密算法是比较常见的,在爬取部分网站时,也可能会遇到这些算法,所以作为爬虫工程师是有必要了解一下这些算法的,如下图所示某网站就使用了 SM2 和 SM4 加密算法:
1.1 算法分类
1.1.1 SM2 椭圆曲线公钥加密算法
SM2 为椭圆曲线(ECC)公钥加密算法,非对称加密,SM2 算法和 RSA算法都是公钥加密算法,SM2 算法是一种更先进安全的算法,在我们国家商用密码体系中被用来替换RSA 算法,在不少官方网站会见到此类加密算法。我国学者对椭圆曲线密码的研究从 20 世纪 80 年代开始,目前已取得不少成果,SM2 椭圆曲线公钥密码算法比 RSA算法有以下优势:
1.1.2SM4分组加密算法
SM4 为无线局域网标准的分组加密算法,对称加密,用于替代 DES/AES 等国际算法,SM4 算法与 AES算法具有相同的密钥长度和分组长度,均为 128 位,故对消息进行加解密时,若消息长度过长,需要进行分组,要消息长度不足,则要进行填充。加密算法与密钥扩展算法都采用 32 轮非线性迭代结构,解密算法与加密算法的结构相同,只是轮密钥的使用顺序相反,解密轮密钥是加密轮密钥的逆序。
二.算法实现
1. JavaScript实现
在 JavaScript 中已有比较成熟的实现库,这里推荐 sm-crypto[4],目前支持SM2、SM3 和 SM4,需要注意的是,SM2 非对称加密的结果由 C1、C2、C3 三部分组成,其中 C1 是生成随机数的计算出的椭圆曲线点,C2是密文数据,C3是SM3 的摘要值,最开始的国密标准的结果是按 C1C2C3 顺序的,新标准的是按 C1C3C2 顺序存放的,sm-crypto 支持设置cipherMode,也就是 C1C2C3 的排列顺序。
以 SM2算法为例,实现如下(其他算法和详细用法可参考其官方文档):
1. SM2
// npm install sm-crypto --save
const sm2 = require('sm-crypto').sm2
// 1 - C1C3C2,0 - C1C2C3,默认为1
const cipherMode = 1
// 获取密钥对
let keypair = sm2.generateKeyPairHex()
let publicKey = keypair.publicKey // 公钥
let privateKey = keypair.privateKey // 私钥
let msgString = "this is the data to be encrypted"
let encryptData = sm2.doEncrypt(msgString, publicKey, cipherMode) // 加密结果
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode) // 解密结果
console.log("encryptData: ", encryptData)
console.log("decryptData: ", decryptData)
2. sm3
const sm3 = require('sm-crypto').sm3;
const data = 'Hello, SM3!';
const hash = sm3(data);
console.log('SM3 Hash:', hash);
3. sm4
const sm4 = require('sm-crypto').sm4;
// 设置SM4密钥(128位,16字节)
const key = '0123456789ABCDEF0123456789ABCDEF';
// 设置SM4加解密模式(ecb、cbc、ctr等)
const mode = 'ecb';
// 加密数据
const plaintext = 'Hello, SM4!';
const ciphertext = sm4.encrypt(plaintext, key, { mode });
console.log('Encrypted:', ciphertext);
// 解密数据
const decryptedText = sm4.decrypt(ciphertext, key, { mode });
console.log('Decrypted:', decryptedText);
2python实现
在 Python 里面并没有比较官方的库来实现国密算法,这里仅列出了其中两个较为完善的第三方库,需要注意的是,SM1 和 SM7 算法不公开,目前大多库仅实现了 SM2、SM3、SM4 三种密算法。
若要使用 SM9 算法,可下载 gmssl-python 源码手动安装。
pip install gmssl
1. sm2
from gmssl import sm2
# 16 进制的公钥和私钥
private_key = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
public_key = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207'
sm2_crypt = sm2.CryptSM2(public_key=public_key, private_key=private_key)
# 待加密数据和加密后数据为 bytes 类型
data = b"this is the data to be encrypted"
enc_data = sm2_crypt.encrypt(data)
dec_data = sm2_crypt.decrypt(enc_data)
print('enc_data: ', enc_data.hex())
print('dec_data: ', dec_data)
2.sm3
- bytes类型的对象,它表示一个包含字节数据的序列。当您使用for 循环遍历 message 时,实际上遍历的是 message 中的每个字节。每个字节都是一个整数,对应于ASCII编码中的字符。所以,在打印每个字节时,您看到的是对应字符的ASCII值
from gmssl import sm3, func
def sm3_hash(message):
# python实现需要把编码数据转换成列表
hash_hex = sm3.sm3_hash(func.bytes_to_list(message))
print(hash_hex)
# main
if __name__ == '__main__':
message = b"123" # bytes类型
sm3_hash(message)
3. sm4
from gmssl import sm4, func
# 创建SM4加密对象
sm4_crypt = sm4.CryptSM4()
key = b'0123456789ABCDEF0123456789ABCDEF'
# 设置密钥
sm4_crypt.set_key(key, sm4.SM4_ENCRYPT)
# 要加密的数据
data = b"Hello, SM4!"
# 加密数据
ciphertext = sm4_crypt.crypt_ecb(func.bytes_to_list(data))
# 将加密后的数据转换为字节串
encrypted_data = bytes(func.list_to_bytes(ciphertext))
# 解密数据(如果需要)
sm4_crypt.set_key(key, sm4.SM4_DECRYPT)
decrypted_data = sm4_crypt.crypt_ecb(ciphertext)
decrypted_data = bytes(func.list_to_bytes(decrypted_data))
print("原始数据:", data.decode("utf-8"))
print("加密后的数据:", encrypted_data.hex())
print("解密后的数据:", decrypted_data.decode("utf-8"))