本项目是python实现的基于密钥分发过程的简易信息管理系统,实现了加密交流,信息录入,信息查看等功能,重点在于密钥分发的流程展示,以体现密钥分发的原理。
密钥分发流程如下
发送:
- 服务器生成共享密钥和随机对称密钥
- 用客户端公钥经sm2算法加密随机对称密钥得到E(对称密钥)
- 用随机对称密钥经sm4算法加密共享密钥得到E(共享密钥)
- 共享密钥经sm3生成摘要,用服务器私钥经sm2算法签名得到 数字签名
- 封装{E(对称密钥)+E(共享密钥)+数字签名}形成数字签名
- 发送至客户端
接收:
- 客户端接收数字信封
- 用客户端私钥经sm2算法解密E(对称密钥)得到随机对称密钥
- 用随机对称密钥经sm4算法解密E(共享密钥)得到共享密钥
- 用发送方公钥解密数字签名得到信息摘要
- 共享密钥经sm3生成信息摘要
- 生成的信息摘要和接受的进行对比相同,则保存共享密钥
工程目录如下:
----客户端
|______userlist.txt
|______client.py
----服务器
|______keylist.txt
|______studentlist.txt
|______server.py
客户端目录下需要有userlist.txt
服务器目录下需要有keylist.txt 和 studentlist.txt
需要国密库,下载命令行:pip install gmssl
server.py
# 导入socket库 和 国密库
from socket import *
from random import SystemRandom
import base64
import binascii
from gmssl import sm2, func
from gmssl import sm3
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
import os.path
import os
import json
filename = 'keylist.txt'
studentfile = 'studentlist.txt'
server_priKey = b'feb8f2803dd43e1754d22f7e761a8bb47da5197220aafdc534c05565d0e7adbe'
server_pubKey = b'd053f19eb5b2fcd94714f8c4f6d4c64c5dc3b2fd4c061896ffb512261f2a6c53b64e67dcc6e66a55db78200900e77563568d0d9990496f5ecd81eba3582f85be'
def show():
name_encode = dataSocket.recv(BUFLEN)
name = name_encode.decode()
gethubkey = []
with open(filename, 'r', encoding='utf-8') as rfile:
date = rfile.readlines()
for item in date:
d = dict(eval(item))
if name != '':
if d['用户名'] == name:
gethubkey.append(d)
for getkey in gethubkey:
hubkey = getkey.get('会话共享密钥')
student_lst = []
if os.path.exists(studentfile):
with open(studentfile, 'r', encoding='utf-8') as rfile:
students = rfile.readlines()
for item in students:
student_lst.append(eval(item))
# 初始化sm4
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(hubkey.encode(), SM4_ENCRYPT)
encrypt_tosend = crypt_sm4.crypt_ecb(str(student_lst).encode())
print('加密后学生信息:', encrypt_tosend)
dataSocket.send(encrypt_tosend)
def insert():
name_encode = dataSocket.recv(BUFLEN)
name = name_encode.decode()
gethubkey = []
with open(filename, 'r', encoding='utf-8') as rfile:
date = rfile.readlines()
for item in date:
d = dict(eval(item))
if name != '':
if d['用户名'] == name:
gethubkey.append(d)
for getkey in gethubkey:
hubkey = getkey.get('会话共享密钥')
student_list = []
recved = dataSocket.recv(BUFLEN)
print('收到加密后学生信息',recved)
# 初始化
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(hubkey.encode(), SM4_DECRYPT)
decrypt_date = crypt_sm4.crypt_ecb(recved)
recv_json = decrypt_date.decode('utf-8')
student = json.loads(recv_json)
print('解密得到学生信息:',student)
student_list.append(student)
savedate(student_list)
send = '信息录入完毕'
dataSocket.send(send.encode())
def savedate(lst): # 保存用户信息
try:
student_txt = open(studentfile, 'a', encoding='utf-8')
except:
student_txt = open(studentfile, 'w', encoding='utf-8')
for item in lst:
student_txt.write(str(item) + '\n') # 将内容写入文件中
def community():
name_encode = dataSocket.recv(BUFLEN)
name = name_encode.decode()
gethubkey = []
while True:
# 尝试读取对方发送的消息
# BUFLEN 指定从接收缓冲里最多读取多少字节
recved = dataSocket.recv(BUFLEN)
# 如果返回空bytes,表示对方关闭了连接
# 退出循环,结束消息收发
if not recved:
break
with open(filename, 'r', encoding='utf-8') as rfile:
date = rfile.readlines()
for item in date:
d = dict(eval(item))
if name != '':
if d['用户名'] == name:
gethubkey.append(d)
for getkey in gethubkey:
hubkey = getkey.get('会话共享密钥')
# 初始化sm4
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(hubkey.encode(), SM4_DECRYPT)
decrypt_date = crypt_sm4.crypt_ecb(recved)
info = decrypt_date.decode()
print(f'收到对方信息密文: {recved}')
print(f'解密后明文为: {info}')
# 从终端读入用户输入的字符串
toSend = input('>>> ')
# 加密后发送
crypt_sm4.set_key(hubkey.encode(), SM4_ENCRYPT)
encrypt_date = crypt_sm4.crypt_ecb(toSend.encode())
dataSocket.send(encrypt_date)
# 登记
def register():
Key = PrivateKey()
hubkey = Key.toString()
print('收到用户信息')
recved = dataSocket.recv(BUFLEN)
# 将二进制数据流转换成字符串
dec_date_json = recved.decode('utf-8')
# 将json格式字符串转换成字典
dec_date = json.loads(dec_date_json)
print(dec_date)
print('生成共享密钥', hubkey)
list = {'用户名':dec_date.get('用户名') , '用户公钥':dec_date.get('用户公钥') ,'会话共享密钥':hubkey}
user_list = []
# 将学生信息添加到列表中
user_list.append(list)
# 调用save()函数
save(user_list)
# 回复客户端
send='登记完成'
dataSocket.send(send.encode())
print('用户登记完毕!!!')
recved = dataSocket.recv(BUFLEN)
dec_recved = recved.decode()
if dec_recved == 'get_hubkey':
print('收到共享密钥获取请求')
# 初始化sm2算法,公钥加密为对方公钥,私钥签名为己方私钥
usr_public_key = dec_date.get('用户公钥')
sm2_crypt = sm2.CryptSM2(public_key=usr_public_key, private_key=server_priKey.decode())
# 共享密钥输入sm3,生成信息摘要
data = hubkey.encode() # bytes类型
summary = sm3.sm3_hash(func.bytes_to_list(data))
# sm2为摘要签名,生成数字签名
sign = sm2_crypt.sign(summary.encode(), server_priKey)
# 生成随机对称密钥
random_key = func.random_hex(sm2_crypt.para_len)
# sm2加密随机密钥
enc_random_key = sm2_crypt.encrypt(random_key.encode())
# sm4加密共享密钥
key = random_key.encode()
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(key, SM4_ENCRYPT)
encrypt_hubkey = crypt_sm4.crypt_ecb(data)
# 生成数字信封,写入字典
digital = {'E(随机密钥)': base64.b64encode(enc_random_key).decode(), 'E(共享密钥)': base64.b64encode(encrypt_hubkey).decode(), '数字签名':sign}
print('以生成数字信封')
print(digital)
# 将字典转换成json格式的字符串
digital_json = json.dumps(digital)
# 将字符串转换成二进制数据流
send_digital = digital_json.encode('utf-8')
# 发送至客户端
dataSocket.send(send_digital)
print('发送完毕')
def save(lst): # 保存用户信息
try:
user_txt = open(filename, 'a', encoding='utf-8')
except:
user_txt = open(filename, 'w', encoding='utf-8')
for item in lst:
user_txt.write(str(item) + '\n') # 将内容写入文件中
# 生成密钥对模块
class CurveFp:
def __init__(self, A, B, P, N, Gx, Gy, name):
self.A = A
self.B = B
self.P = P
self.N = N
self.Gx = Gx
self.Gy = Gy
self.name = name
sm2p256v1 = CurveFp(
name="sm2p256v1",
A=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC,
B=0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93,
P=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF,
N=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123,
Gx=0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7,
Gy=0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
)
def multiply(a, n, N, A, P):
return fromJacobian(jacobianMultiply(toJacobian(a), n, N, A, P), P)
def add(a, b, A, P):
return fromJacobian(jacobianAdd(toJacobian(a), toJacobian(b), A, P), P)
def inv(a, n):
if a == 0:
return 0
lm, hm = 1, 0
low, high = a % n, n
while low > 1:
r = high // low
nm, new = hm - lm * r, high - low * r
lm, low, hm, high = nm, new, lm, low
return lm % n
def toJacobian(Xp_Yp):
Xp, Yp = Xp_Yp
return (Xp, Yp, 1)
def fromJacobian(Xp_Yp_Zp, P):
Xp, Yp, Zp = Xp_Yp_Zp
z = inv(Zp, P)
return ((Xp * z ** 2) % P, (Yp * z ** 3) % P)
def jacobianDouble(Xp_Yp_Zp, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
if not Yp:
return (0, 0, 0)
ysq = (Yp ** 2) % P
S = (4 * Xp * ysq) % P
M = (3 * Xp ** 2 + A * Zp ** 4) % P
nx = (M ** 2 - 2 * S) % P
ny = (M * (S - nx) - 8 * ysq ** 2) % P
nz = (2 * Yp * Zp) % P
return (nx, ny, nz)
def jacobianAdd(Xp_Yp_Zp, Xq_Yq_Zq, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
Xq, Yq, Zq = Xq_Yq_Zq
if not Yp:
return (Xq, Yq, Zq)
if not Yq:
return (Xp, Yp, Zp)
U1 = (Xp * Zq ** 2) % P
U2 = (Xq * Zp ** 2) % P
S1 = (Yp * Zq ** 3) % P
S2 = (Yq * Zp ** 3) % P
if U1 == U2:
if S1 != S2:
return (0, 0, 1)
return jacobianDouble((Xp, Yp, Zp), A, P)
H = U2 - U1
R = S2 - S1
H2 = (H * H) % P
H3 = (H * H2) % P
U1H2 = (U1 * H2) % P
nx = (R ** 2 - H3 - 2 * U1H2) % P
ny = (R * (U1H2 - nx) - S1 * H3) % P
nz = (H * Zp * Zq) % P
return (nx, ny, nz)
def jacobianMultiply(Xp_Yp_Zp, n, N, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
if Yp == 0 or n == 0:
return (0, 0, 1)
if n == 1:
return (Xp, Yp, Zp)
if n < 0 or n >= N:
return jacobianMultiply((Xp, Yp, Zp), n % N, N, A, P)
if (n % 2) == 0:
return jacobianDouble(jacobianMultiply((Xp, Yp, Zp), n // 2, N, A, P), A, P)
if (n % 2) == 1:
return jacobianAdd(jacobianDouble(jacobianMultiply((Xp, Yp, Zp), n // 2, N, A, P), A, P), (Xp, Yp, Zp), A, P)
class PrivateKey:
def __init__(self, curve=sm2p256v1, secret=None):
self.curve = curve
self.secret = secret or SystemRandom().randrange(1, curve.N)
def publicKey(self):
curve = self.curve
xPublicKey, yPublicKey = multiply((curve.Gx, curve.Gy), self.secret, A=curve.A, P=curve.P, N=curve.N)
return PublicKey(xPublicKey, yPublicKey, curve)
def toString(self):
return "{}".format(str(hex(self.secret))[2:].zfill(64))
class PublicKey:
def __init__(self, x, y, curve):
self.x = x
self.y = y
self.curve = curve
def toString(self, compressed=True):
return {
True: str(hex(self.x))[2:],
False: "{}{}".format(str(hex(self.x))[2:].zfill(64), str(hex(self.y))[2:].zfill(64))
}.get(compressed)
def create_key_pair():
priKey = PrivateKey()
pubKey = priKey.publicKey()
return priKey.toString(), pubKey.toString(compressed=False)
# 主机地址为空字符串,表示绑定本机所有网络接口ip地址
# 等待客户端来连接
IP = '127.0.0.1'
# 端口号
PORT = 4000
# 定义一次从socket缓冲区最多读入512个字节数据
BUFLEN = 2048
# 实例化一个socket对象
# 参数 AF_INET 表示该socket网络层使用IP协议
# 参数 SOCK_STREAM 表示该socket传输层使用TCP协议
listenSocket = socket(AF_INET, SOCK_STREAM)
# socket绑定地址和端口
listenSocket.bind((IP, PORT))
# 使socket处于监听状态,等待客户端的连接请求
# 参数 5 表示 最多接受多少个等待连接的客户端
listenSocket.listen(5)
print(f'服务端启动成功,在{PORT}端口等待客户端连接...')
dataSocket, addr = listenSocket.accept()
print('接受一个客户端连接:', addr)
while True:
# 尝试读取对方发送的消息
# BUFLEN 指定从接收缓冲里最多读取多少字节
recved = dataSocket.recv(BUFLEN)
# 如果返回空bytes,表示对方关闭了连接
# 退出循环,结束消息收发
if not recved:
break
# 读取的字节数据是bytes类型,需要解码为字符串
info = recved.decode()
if info == 'user_reg':
print('收到用户登记请求')
print('准备登记')
# 回复客户端
send = '登记中'
dataSocket.send(send.encode())
# 登记用户
register()
continue
if info == 'community':
community()
if info == 'insert':
insert()
if info == 'show':
show()
dataSocket.close()
listenSocket.close()
client.py
# 导入socket库 和 国密库
from socket import *
from random import SystemRandom
import base64
import binascii
from gmssl import sm2, func
from gmssl import sm3
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
import os.path
import os
import json
# 生成密钥对模块
class CurveFp:
def __init__(self, A, B, P, N, Gx, Gy, name):
self.A = A
self.B = B
self.P = P
self.N = N
self.Gx = Gx
self.Gy = Gy
self.name = name
sm2p256v1 = CurveFp(
name="sm2p256v1",
A=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC,
B=0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93,
P=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF,
N=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123,
Gx=0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7,
Gy=0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
)
def multiply(a, n, N, A, P):
return fromJacobian(jacobianMultiply(toJacobian(a), n, N, A, P), P)
def add(a, b, A, P):
return fromJacobian(jacobianAdd(toJacobian(a), toJacobian(b), A, P), P)
def inv(a, n):
if a == 0:
return 0
lm, hm = 1, 0
low, high = a % n, n
while low > 1:
r = high // low
nm, new = hm - lm * r, high - low * r
lm, low, hm, high = nm, new, lm, low
return lm % n
def toJacobian(Xp_Yp):
Xp, Yp = Xp_Yp
return (Xp, Yp, 1)
def fromJacobian(Xp_Yp_Zp, P):
Xp, Yp, Zp = Xp_Yp_Zp
z = inv(Zp, P)
return ((Xp * z ** 2) % P, (Yp * z ** 3) % P)
def jacobianDouble(Xp_Yp_Zp, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
if not Yp:
return (0, 0, 0)
ysq = (Yp ** 2) % P
S = (4 * Xp * ysq) % P
M = (3 * Xp ** 2 + A * Zp ** 4) % P
nx = (M ** 2 - 2 * S) % P
ny = (M * (S - nx) - 8 * ysq ** 2) % P
nz = (2 * Yp * Zp) % P
return (nx, ny, nz)
def jacobianAdd(Xp_Yp_Zp, Xq_Yq_Zq, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
Xq, Yq, Zq = Xq_Yq_Zq
if not Yp:
return (Xq, Yq, Zq)
if not Yq:
return (Xp, Yp, Zp)
U1 = (Xp * Zq ** 2) % P
U2 = (Xq * Zp ** 2) % P
S1 = (Yp * Zq ** 3) % P
S2 = (Yq * Zp ** 3) % P
if U1 == U2:
if S1 != S2:
return (0, 0, 1)
return jacobianDouble((Xp, Yp, Zp), A, P)
H = U2 - U1
R = S2 - S1
H2 = (H * H) % P
H3 = (H * H2) % P
U1H2 = (U1 * H2) % P
nx = (R ** 2 - H3 - 2 * U1H2) % P
ny = (R * (U1H2 - nx) - S1 * H3) % P
nz = (H * Zp * Zq) % P
return (nx, ny, nz)
def jacobianMultiply(Xp_Yp_Zp, n, N, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
if Yp == 0 or n == 0:
return (0, 0, 1)
if n == 1:
return (Xp, Yp, Zp)
if n < 0 or n >= N:
return jacobianMultiply((Xp, Yp, Zp), n % N, N, A, P)
if (n % 2) == 0:
return jacobianDouble(jacobianMultiply((Xp, Yp, Zp), n // 2, N, A, P), A, P)
if (n % 2) == 1:
return jacobianAdd(jacobianDouble(jacobianMultiply((Xp, Yp, Zp), n // 2, N, A, P), A, P), (Xp, Yp, Zp), A, P)
class PrivateKey:
def __init__(self, curve=sm2p256v1, secret=None):
self.curve = curve
self.secret = secret or SystemRandom().randrange(1, curve.N)
def publicKey(self):
curve = self.curve
xPublicKey, yPublicKey = multiply((curve.Gx, curve.Gy), self.secret, A=curve.A, P=curve.P, N=curve.N)
return PublicKey(xPublicKey, yPublicKey, curve)
def toString(self):
return "{}".format(str(hex(self.secret))[2:].zfill(64))
class PublicKey:
def __init__(self, x, y, curve):
self.x = x
self.y = y
self.curve = curve
def toString(self, compressed=True):
return {
True: str(hex(self.x))[2:],
False: "{}{}".format(str(hex(self.x))[2:].zfill(64), str(hex(self.y))[2:].zfill(64))
}.get(compressed)
def create_key_pair():
priKey = PrivateKey()
pubKey = priKey.publicKey()
return priKey.toString(), pubKey.toString(compressed=False)
# 用户界面
def loginmenm(): #登录
print('==========================加密通信系统==========================')
print('---------------------------登录界面----------------------------')
print('\t\t\t 1.登录')
print('\t\t\t 2.注册')
print('\t\t\t 0.退出系统')
print('-------------------------------------------------------------------')
def menm(): # 菜单
print('==========================加密通信系统==========================')
print('---------------------------功能选项----------------------------')
print('\t\t\t 1.与服务器通信')
print('\t\t\t 2.录入学生信息')
print('\t\t\t 3.查看学生信息')
print('\t\t\t 0.退出登录')
print('-------------------------------------------------------------------')
# 文件名
filename = 'userlist.txt'
# 服务器公钥
server_pubKey = b'd053f19eb5b2fcd94714f8c4f6d4c64c5dc3b2fd4c061896ffb512261f2a6c53b64e67dcc6e66a55db78200900e77563568d0d9990496f5ecd81eba3582f85be'
IP = '127.0.0.1'
SERVER_PORT = 4000
BUFLEN = 2048
# 实例化一个socket对象,指明协议
dataSocket = socket(AF_INET, SOCK_STREAM)
# 连接服务端socket
dataSocket.connect((IP, SERVER_PORT))
def main(): # 主程序
while True:
global num
num = '0'
loginmenm()
choice = int(input('请选择>>>'))
if choice in [0, 1, 2]:
if choice == 0:
answer = input('您确定要退出系统吗?y/n>>>')
if answer == 'y' or answer == 'Y':
print('谢谢您的使用!!!')
break # 退出系统
else:
continue
elif choice == 2:
register()
elif choice == 1:
login()
while num != '0':
menm()
choice = int(input('请选择>>>'))
if choice in [0, 1, 2, 3]:
if choice == 0:
answer = input('您确定要退出登录吗?y/n>>>')
if answer == 'y' or answer == 'Y':
print('谢谢您的使用!!!')
break # 退出系统
else:
continue
elif choice == 1:
community(num) # 与服务器通信
elif choice == 2:
insert(num)
elif choice == 3:
show(num)
def insert(num): # 插入信息
send = 'insert'
dataSocket.send(send.encode())
sendname = num
dataSocket.send(sendname.encode())
name = num
gethubkey = []
with open(filename, 'r', encoding='utf-8') as rfile:
date = rfile.readlines()
for item in date:
d = dict(eval(item))
if name != '':
if d['用户名'] == num:
gethubkey.append(d)
for getkey in gethubkey:
hubkey = getkey.get('会话共享密钥')
while True: # 循环输入
id = input('请输入ID(如1001):')
if not id: # 防止手滑
break
name = input('请输入姓名:')
if not name:
break
try:
english = int(input('请输入英语成绩:'))
python = int(input('请输入python成绩:'))
java = int(input('请输入Java成绩:'))
except:
print('输入无效,不是整数类型,请重新输入')
continue
# 将录入的学生信息保存到字典当中
student = {'id': id, 'name': name, 'english': english, 'python': python, 'java': java}
answer = input('是否继续添加?y/n\n')
if answer == 'y':
continue
else:
break
# 将字典转换成json格式的字符串
student_json = json.dumps(student)
# 将字符串转换成二进制数据流
send_data = student_json.encode('utf-8')
# 初始化sm4
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(hubkey.encode(), SM4_ENCRYPT)
encrypt_tosend = crypt_sm4.crypt_ecb(send_data)
print('发送加密后学生信息:', encrypt_tosend)
# 发送数据
dataSocket.send(encrypt_tosend)
recved = dataSocket.recv(BUFLEN)
print('收到浏览器回复:', recved.decode())
def show(num):
send = 'show'
dataSocket.send(send.encode())
sendname = num
dataSocket.send(sendname.encode())
name = num
gethubkey = []
with open(filename, 'r', encoding='utf-8') as rfile:
date = rfile.readlines()
for item in date:
d = dict(eval(item))
if name != '':
if d['用户名'] == num:
gethubkey.append(d)
for getkey in gethubkey:
hubkey = getkey.get('会话共享密钥')
student_recv = dataSocket.recv(BUFLEN)
print('收到加密学生信息:', student_recv)
print('解密结果为')
# 初始化
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(hubkey.encode(), SM4_DECRYPT)
decrypt_date = crypt_sm4.crypt_ecb(student_recv)
students = decrypt_date.decode()
if students:
show_student(eval(students))
def show_student(lst):
if len(lst) == 0:
print('没有查询到学生信息,无数据显示!!!')
return
# 定义标题显示格式
format_title = '{:^6}\t{:^12}\t{:^8}\t{:^10}\t{:^10}\t{:^8}'
print(format_title.format('ID', '姓名', '英语成绩', 'python成绩', 'java成绩', '总成绩'))
# 定义内容的显示格式
format_data = '{:^6}\t{:^12}\t{:^8}\t{:^10}\t{:^10}\t{:^8}'
for item in lst:
print(format_data.format(item.get('id'),
item.get('name'),
item.get('english'),
item.get('python'),
item.get('java'),
int(item.get('english')) + int(item.get('python')) + int(item.get('java'))
))
def community(num):
send = 'community'
dataSocket.send(send.encode())
sendname = num
dataSocket.send(sendname.encode())
name = num
gethubkey = []
while True:
# 从终端读入用户输入的字符串
toSend = input('>>> ')
if toSend == 'exit':
break
# 获取共享密钥
with open(filename, 'r', encoding='utf-8') as rfile:
date = rfile.readlines()
for item in date:
d = dict(eval(item))
if name != '':
if d['用户名'] == num:
gethubkey.append(d)
for getkey in gethubkey:
hubkey = getkey.get('会话共享密钥')
# 初始化sm4
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(hubkey.encode(), SM4_ENCRYPT)
encrypt_tosend = crypt_sm4.crypt_ecb(toSend.encode())
dataSocket.send(encrypt_tosend)
# 等待接收服务端的消息
recved = dataSocket.recv(BUFLEN)
# 如果返回空bytes,表示对方关闭了连接
if not recved:
break
# 打印读取的信息
crypt_sm4.set_key(hubkey.encode(), SM4_DECRYPT)
decrypt_date = crypt_sm4.crypt_ecb(recved)
info = decrypt_date.decode()
print(f'收到对方信息密文: {recved}')
print(f'解密后明文为: {info}')
def login(): # 登录
password_query = []
global num
while True:
if os.path.exists(filename): # 如果可以打开文件
name = input('请输入用户名:')
psd = input('请输入密码:')
gethubkey = []
with open(filename, 'r', encoding='utf-8') as rfile:
date = rfile.readlines()
for item in date:
d = dict(eval(item))
if name != '':
if d['用户名'] == name:
gethubkey.append(d)
for getkey in gethubkey:
hubkey = getkey.get('会话共享密钥')
with open(filename, 'r', encoding='utf-8') as rfile:
date = rfile.readlines()
for item in date:
d = dict(eval(item))
if name != '':
if d['用户名'] == name:
password_query.append(d)
for getpassword in password_query:
getpd = getpassword.get('密码')
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(hubkey.encode(), SM4_DECRYPT)
decrypt_value = crypt_sm4.crypt_ecb(getpd)
if psd == decrypt_value.decode():
print('密码正确')
num = name
return num
else:
print('输入错误')
num = '0'
return num
def register(): # 注册
user_list = []
# 为新用户生成密钥对
priKey = PrivateKey()
pubKey = priKey.publicKey()
while True: # 循环输入
username = input('请输入用户名:')
if not username: # 防止手滑
break
password = input('请输入密码:')
if not password: # 防止手滑
break
# 发送用户信息至服务器登记
print('发送用户登记请求')
send_register = 'user_reg'
dataSocket.send(send_register.encode())
recved = dataSocket.recv(BUFLEN)
print('收到服务器回复:',recved.decode()) #登记中
# 发送用户信息
print('发送用户信息')
# 写入字典
send_list= {'用户名': username,'用户公钥':pubKey.toString(compressed=False)}
# 将字典转换成json格式的字符串
send_list_json= json.dumps(send_list)
# 将字符串转换成二进制数据流
enc_send_list= send_list_json.encode('utf-8')
# 发送用户数据流
dataSocket.send(enc_send_list)
recved = dataSocket.recv(BUFLEN)
print('收到服务器回复:',recved.decode()) #登记完成
# 发送共享密钥请求
print('发送共享密钥获取请求')
toSend = 'get_hubkey'
enc_toSend = toSend.encode()
dataSocket.send(enc_toSend)
# 获取服务器回复
recved = dataSocket.recv(BUFLEN)
# 将二进制数据流转换成字符串
dec_date_json = recved.decode('utf-8')
# 将json格式字符串转换成字典
digital = json.loads(dec_date_json)
print('收到服务器回复:', digital)
# 初始化sm2算法,己方私钥解密,对方公钥验签
sm2_crypt = sm2.CryptSM2(public_key=server_pubKey.decode(), private_key=priKey.toString())
# 获得随机密钥
enc_random_key = digital.get('E(随机密钥)')
dec_random_key = sm2_crypt.decrypt(base64.b64decode(enc_random_key.encode())) # byte
# 获得共享密钥
encrypt_hubkey = digital.get('E(共享密钥)')
key = dec_random_key
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(key, SM4_DECRYPT)
decrypt_hubkey = crypt_sm4.crypt_ecb(base64.b64decode(encrypt_hubkey.encode())) # byte
# 生成共享密钥的信息摘要
summary = sm3.sm3_hash(func.bytes_to_list(decrypt_hubkey))
# 验证签名
sign = digital.get('数字签名')
result = sm2_crypt.verify(sign, summary.encode())
if result:
print('验证成功,收到共享密钥为:',decrypt_hubkey.decode())
else:
print('验证失败')
# 用共享密钥加密密码
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(decrypt_hubkey, SM4_ENCRYPT)
encrypt_password = crypt_sm4.crypt_ecb(password.encode())
# 将录入的学生信息保存到字典当中
list = {'用户名': username, '密码': encrypt_password,'用户公钥':pubKey.toString(compressed=False) , '用户私钥':priKey.toString() , '会话共享密钥':decrypt_hubkey.decode()}
# 将学生信息添加到列表中
user_list.append(list)
answer = input('是否继续添加?y/n\n')
if answer == 'y':
continue
else:
break
# 调用save()函数
save(user_list)
print('用户注册完毕!!!''')
def save(lst): # 保存信息
try:
user_txt = open(filename, 'a', encoding='utf-8')
except:
user_txt = open(filename, 'w', encoding='utf-8')
for item in lst:
user_txt.write(str(item) + '\n') # 将内容写入文件中
if __name__ == '__main__':
main()
dataSocket.close()
主要功能的流程图
用户注册流程图:
- 用户选择注册功能,输入用户名和密码
- 为新用户生成密钥对,并向服务器发送登记请求
- 服务器接收登记请求,调用登记函数,并回复客户端
- 客户端接收回复,并将用户名和公钥,发送至服务器
- 服务器接收用户信息,并生成共享密钥
- 将用户信息和共享密钥写入密钥列表文件,回复客户端登记完成
- 客户端收到回复,并发送获取共享密钥请求
- 服务器接收请求,走密钥分发的发送流程
- 客户端走密钥分发的接收流程,获取共享密钥
- 客户端将用户所有信息和共享密钥写入用户列表文件
登录和查看信息流程图:
(1)客户端用户登录,输入用户名和密码,读取用户列表文件,获取加密的密码,解密对比验证密码
(2)登录成功,选择学生信息查看功能,向服务器发送查看请求
(3)服务器接收请求运行查看函数
(4)读取学生信息文件,并用共享密钥加密,发送至客户端
(5)客户端用共享密钥解密信息,并打印
登录和加密交流流程图:
- 客户端用户登录,输入用户名和密码,读取用户列表文件,获取加密的密码,解密对比验证密码
- 登录成功,选择加密交流功能,向服务器发送交流请求
- 服务器接收交流请求运行交流函数
- 客户端输入信息,并用共享密钥加密,发送至服务器
- 服务器根据用户名,读取密钥列表文件,获取共享密钥,解密得到信息
- 服务器用对应的共享密钥加密信息进行回复
- 循环交流
- 客户端输入exit可退出交流功能
登录和录入信息流程图:
(1)客户端用户登录,输入用户名和密码,读取用户列表文件,获取加密的密码,解密对比验证密码
(2)登录成功,选择学生信息录入功能,向服务器发送录入请求
(3)服务器接收录入请求,运行录入函数
(4)客户端输入学生信息,用共享密钥加密发送至服务器
(5)服务器接收加密信息,用共享密钥进行解密,将得到的学生信息写入,学生信息文件,并回复客户端录入完成
(5)客户端接收回复,提示用户是否继续录入