应用密码学课程设计 密钥分发

本项目是python实现的基于密钥分发过程的简易信息管理系统,实现了加密交流,信息录入,信息查看等功能,重点在于密钥分发的流程展示,以体现密钥分发的原理。

密钥分发流程如下

发送:

  1. 服务器生成共享密钥和随机对称密钥
  2. 用客户端公钥经sm2算法加密随机对称密钥得到E(对称密钥)
  3. 用随机对称密钥经sm4算法加密共享密钥得到E(共享密钥)
  4. 共享密钥经sm3生成摘要,用服务器私钥经sm2算法签名得到 数字签名
  5. 封装{E(对称密钥)+E(共享密钥)+数字签名}形成数字签名
  6. 发送至客户端

接收:

  1. 客户端接收数字信封
  2. 用客户端私钥经sm2算法解密E(对称密钥)得到随机对称密钥
  3. 用随机对称密钥经sm4算法解密E(共享密钥)得到共享密钥
  4. 用发送方公钥解密数字签名得到信息摘要
  5. 共享密钥经sm3生成信息摘要
  6. 生成的信息摘要和接受的进行对比相同,则保存共享密钥

工程目录如下:

----客户端

         |______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. 服务器接收用户信息,并生成共享密钥
  6. 将用户信息和共享密钥写入密钥列表文件,回复客户端登记完成
  7. 客户端收到回复,并发送获取共享密钥请求
  8. 服务器接收请求,走密钥分发的发送流程
  9. 客户端走密钥分发的接收流程,获取共享密钥
  10. 客户端将用户所有信息和共享密钥写入用户列表文件

登录和查看信息流程图: 

 

(1)客户端用户登录,输入用户名和密码,读取用户列表文件,获取加密的密码,解密对比验证密码

(2)登录成功,选择学生信息查看功能,向服务器发送查看请求

(3)服务器接收请求运行查看函数

(4)读取学生信息文件,并用共享密钥加密,发送至客户端

(5)客户端用共享密钥解密信息,并打印

登录和加密交流流程图: 

  1. 客户端用户登录,输入用户名和密码,读取用户列表文件,获取加密的密码,解密对比验证密码
  2. 登录成功,选择加密交流功能,向服务器发送交流请求
  3. 服务器接收交流请求运行交流函数
  4. 客户端输入信息,并用共享密钥加密,发送至服务器
  5. 服务器根据用户名,读取密钥列表文件,获取共享密钥,解密得到信息
  6. 服务器用对应的共享密钥加密信息进行回复
  7. 循环交流
  8. 客户端输入exit可退出交流功能

登录和录入信息流程图: 

(1)客户端用户登录,输入用户名和密码,读取用户列表文件,获取加密的密码,解密对比验证密码

(2)登录成功,选择学生信息录入功能,向服务器发送录入请求

(3)服务器接收录入请求,运行录入函数

(4)客户端输入学生信息,用共享密钥加密发送至服务器

(5)服务器接收加密信息,用共享密钥进行解密,将得到的学生信息写入,学生信息文件,并回复客户端录入完成

(5)客户端接收回复,提示用户是否继续录入

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值