基于OpenSSL(GMSSL)的国密算法的应用
===
基于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 系统中的应用探讨 朱泽智 冯燕强 冯智明 桂广网技专栏

浙公网安备 33010602011771号