===
基于OpenSSL(GMSSL)的国密算法的应用
1.gmssl介绍
GmSSL是一个开源的加密包的python实现,支持SM2/SM3/SM4等国密(国家商用密码)算法、项目采用对商业应用友好的类BSD开源许可证,开源且可以用于闭源的商业应用。
2.安装gmssl包
相关包网址:https://pypi.org/project/gmssl/
在终端输入:pip install gmssl
进行安装
3.国密算法调用实现
3.1sm2test.py
import sys
import os
import base64
import binascii
import sm2
import random
import string
import math
import sys
import os
import binascii
import struct
encoding = 'utf-8'
def SM2_enc(plaintext, pk,sk):
sm2_crypt = sm2.CryptSM2(public_key=pk, private_key=sk)
ciphertext=sm2_crypt.encrypt(plaintext)
return ciphertext
# SM2 decryption
def SM2_dec(ciphertext, sk,pk):
sm2_crypt = sm2.CryptSM2(public_key=pk, private_key=sk)
plaintext = sm2_crypt.decrypt(ciphertext)
return plaintext
# SM2 experiment with string
#密钥生成
# key generation
def SM2_Mulyipoint(k, P, a, p): # 多倍点运算
k_b = bin(k).replace('0b', '') # 按2^i分层逐层运算
i = len(k_b)-1
R = P
if i > 0:
k = k-2**i
while i > 0:
R = SM2_Pluspoint(R, R, a, p)
i -= 1
if k > 0:
R = SM2_Pluspoint(R, SM2_Mulyipoint(k, P, a, p), a, p)
return R
def SM2_Pluspoint(P, Q, a, p): # 双倍点运算
if (math.isinf(P[0]) or math.isinf(P[1])) and (~math.isinf(Q[0]) and ~math.isinf(Q[1])): # OP = P
R = Q
elif (~math.isinf(P[0]) and ~math.isinf(P[1])) and (math.isinf(Q[0]) or math.isinf(Q[1])): # PO = P
R = P
elif (math.isinf(P[0]) or math.isinf(P[1])) and (math.isinf(Q[0]) or math.isinf(Q[1])): # OO = O
R = [float('inf'), float('inf')]
else:
if P != Q:
l = SM2__Mod_Decimal(Q[1]-P[1], Q[0]-P[0], p)
else:
l = SM2__Mod_Decimal(3*P[0]**2+a, 2*P[1], p)
x = SM2_Mod(l**2-P[0]-Q[0], p)
y = SM2_Mod(l*(P[0]-x)-P[1], p)
R = [x, y]
return R
def SM2_Mod(a, b): # 摸运算
if math.isinf(a):
return float('inf')
else:
return a % b
def SM2__Mod_Decimal(n, d, b): # 小数的模运算
if d == 0:
x = float('inf')
elif n == 0:
x = 0
else:
a = bin(b-2).replace('0b', '')
y = 1
i = 0
while i < len(a): # n/d = x mod b => x = n*d^(b-2) mod b
y = (y**2) % b # 快速指数运算
if a[i] == '1':
y = (y*d) % b
i += 1
x = (y*n) % b
return x
def key_gen(a, p, n, G): # SM2密钥对的生成
sk = random.randint(1, n-2)
pk = SM2_Mulyipoint(sk, G, a, p)
return sk, pk
def write_key():
p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
Gx = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
Gy = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
G = [Gx, Gy]
[sk, pk] = key_gen(a, p, n, G)
fw = open("sm2d.txt", 'w')
fw.write("%X" % sk)
fw = open("sm2p.txt", 'w')
fw.write("%X%X" % (pk[0], pk[1]))
def bytes2hex(bytesData):
hex = binascii.hexlify(bytesData)
return hex.decode()
def exp_SM2_str(plaintext):
plaintext_bytes = bytes(plaintext,encoding='utf-8')
return plaintext_bytes
def sm2e(emm):
write_key()
sf = open("sm2d.txt", encoding='utf-8')
sk = (sf.read())
print("puba为:", sk)
f = open("sm2p.txt", encoding='utf-8')
pk = (f.read())
print("pria为:", pk)
sm2_crypt = sm2.CryptSM2(public_key=pk, private_key=sk)
plaintext_bytes = exp_SM2_str(emm)
ciphertext = SM2_enc(plaintext_bytes, pk,sk)
return ciphertext
def sm2ee(emm):
sf = open("sm2d.txt", encoding='utf-8')
sk = (sf.read())
f = open("sm2p.txt", encoding='utf-8')
pk = (f.read())
sm2_crypt = sm2.CryptSM2(public_key=pk, private_key=sk)
plaintext_bytes = exp_SM2_str(emm)
ciphertext = SM2_enc(plaintext_bytes, pk, sk)
return ciphertext
def sm2d(ecm):
sf = open("sm2d.txt", encoding='utf-8')
sk =sf.read()
print("puba为:", sk)
f = open("sm2p.txt", encoding='utf-8')
pk = (f.read())
print("pria为:", pk)
sm2_crypt = sm2.CryptSM2(public_key=pk, private_key=sk)
plaintext = SM2_dec(ecm, sk,pk)
return plaintext
3.2 sm3test.py
import sm3
def sm3_hash(message:bytes):
"""
国密sm3加密
:param message: 消息值,bytes类型
:return: 哈希值
"""
msg_list = [i for i in message]
hash_hex = sm3.sm3_hash(msg_list)
print(hash_hex)
# bytes2hex(hash_hex);
hash_bytes = bytes.fromhex(hash_hex)
print(hash_bytes)
# return bytes.hash
# return hash
3.3sm4test.py
import string
import random
from sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
import func
crypt_sm4 = CryptSM4()
def gen_key(bits):
#key = random.randbytes(15)
#key = random.sample('zyxwvutsrqponmlkjihgfedcba1234567890', 5)
num_set = [chr(i) for i in range(48, 58)]
char_set = [chr(i) for i in range(97, 123)]
total_set = num_set + char_set
key = "".join(random.sample(total_set, bits))
#key = ''.join(random.sample(string.ascii_letters + string.digits, 15))
return key
def iv_gen(bits):
num_set = [chr(i) for i in range(48, 58)]
char_set = [chr(i) for i in range(97, 123)]
total_set = num_set + char_set
iv = "".join(random.sample(total_set, bits))
#iv = ''.join(random.sample(string.ascii_letters + string.digits, 15))
return iv
def SM4_ecb_enc(plaintext, key):
crypt_sm4.set_key(key, SM4_ENCRYPT)
ciphertext_bytes = crypt_sm4.crypt_ecb(plaintext)
return ciphertext_bytes
def SM4_ecb_dec(ciphertext, key):
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(key, SM4_DECRYPT)
plaintext_bytes = crypt_sm4.crypt_ecb(ciphertext)
return plaintext_bytes
def SM4_cbc_enc(plaintext, iv, key):
crypt_sm4.set_key(key, SM4_ENCRYPT)
ciphertext_bytes = crypt_sm4.crypt_cbc(iv, plaintext) # bytes类型
return ciphertext_bytes
def SM4_cbc_dec(ciphertext, iv, key):
crypt_sm4.set_key(key, SM4_DECRYPT)
plaintext_bytes = crypt_sm4.crypt_cbc(iv, ciphertext)
return plaintext_bytes
def exp_SM4_str(plaintext):
plaintext_bytes = bytes(plaintext, encoding="utf8")
return plaintext_bytes
def exp_SM4_file(filename):
with open(filename, "rb") as f:
plaintext_bytes = f.read()
f.close()
return plaintext_bytes
def get_key():
key = gen_key(16)
key_bytes = bytes(key, encoding="utf8")
with open('key_bytes.txt', 'wb') as f:
f.truncate()
f.write(key_bytes)
f.close
print("key为:", key)
return key_bytes
def get_iv():
iv = iv_gen(16)
iv_bytes = bytes(iv, encoding="utf8")
return iv_bytes
4.功能设计
1.用户注册时,系统随机生成一个salt值,将salt与用户输入的口令拼接后利用sm3算法计算hash值,将salt、hash、用户名存储在数据库中。
2.用户登录时,客户端将用户输入的用户名传到服务端,服务端根据用户名检索数据库,取出对应的salt和hash。
3.服务端利用sm4算法生成对称密钥SK,用SK给salt加密得到密文ECM,再将ECM和SK一起用sm2客户端公钥进行加密得到密文EMM并传给客户端。
4.客户端用自己的sm2私钥解密后得到ECM和SK,再用SK解密ECM得到salt。
5.客户端用salt和用户输入的口令拼接后利用sm3算法计算新hash值,并将新hash值传给服务端。
6.服务端将两个hash值进行比较,将结果返回给客户端。
7.若两个hash值相同,说明用户输入口令正确,可以登录;反之,说明用户输入口令不正确,提示用户重新输入口令。
5.项目代码
由于本项目主要研究gmssl的应用,客户端、服务端、数据库的搭建并不是重点,因此本项目暂时没有实现这几个功能,选择用本地文件模拟数据库的存取。
项目代码已发至码云:https://gitee.com/yu-yingpeng/gmssl/
6.运行示例
6.1注册
6.2登录成功
6.3登录失败
7.参考文献:
1.在Python中运行gmssl(https://www.cnblogs.com/rocedu/p/15518988.html)
2.国密算法在 CAS 系统中的应用探讨 朱泽智 冯燕强 冯智明 桂广网技专栏