From 2d3cde265816bfd75dbf53bb1b5e8370a68dae29 Mon Sep 17 00:00:00 2001 From: Q37ek6wrm <1641520215@qq.com> Date: Fri, 16 Dec 2022 20:25:23 +0800 Subject: [PATCH] ADD file via upload --- Client.py | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 Client.py diff --git a/Client.py b/Client.py new file mode 100644 index 0000000..9e89bcd --- /dev/null +++ b/Client.py @@ -0,0 +1,273 @@ +import struct +import sys +from tkinter import * +import time +from tkinter import filedialog +import pickle +import socket +import threading +import tkinter.messagebox +import AESalgorithm +import RSAalgorithm +import hashalg +import generateKey +from tkinter import ttk +import json +import os +import base64 + +# 使用tkinter建立GUI + +IP = '127.0.0.1' +PORT = 4396 +BUFF = 5120 +FIP='127.0.0.1' +FPORT=7932 + +# SERVERPUBLIC +# CLIENTPUBLIC +# CLIENTPRIVATE +def initKey():#生成公私钥 + global CLIENTPUBLIC, CLIENTPRIVATE + (CLIENTPRIVATE, CLIENTPUBLIC) = generateKey.generateMyKey("keys/client/client") + +def fileEncrypt(data):#文件加密打包 + onceKey = AESalgorithm.genKey() + print("发送的密钥",onceKey) + digest = RSAalgorithm.RsaSignal(data, CLIENTPRIVATE) + message = {'Message': base64.b64encode(data), 'digest': digest.decode("utf-8")} # 把消息和摘要打包 + message = pickle.dumps(message) # 转成json字符串 + message = AESalgorithm.AesEncrypt(message, onceKey) + encrykey = RSAalgorithm.RsaEncrypt(onceKey, SERVERPUBLIC) + MES = pickle.dumps([message, encrykey.decode('utf-8')]) + return MES +def initSendSocket(filepath): #文件的发送 + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #创建套接字 + s.connect((FIP, FPORT))#连接 + except socket.error as msg: + print(msg) + sys.exit(1) + print(s.recv(10240)) + # 需要传输的文件路径 + # 判断是否为文件 + if os.path.isfile(filepath): + # 定义定义文件信息。128s表示文件名为128bytes长,l表示一个int或log文件类型,在此为文件大小 + fileinfo_size = struct.calcsize('128sl') + # 定义文件头信息,包含文件名和文件大小 + fhead = struct.pack('128sl', os.path.basename(filepath).encode('utf-8'), os.stat(filepath).st_size) + # 发送文件名称与文件大小 + s.send(fhead) + # 将传输文件以二进制的形式分多次上传至服务器 + fp = open(filepath, 'rb') + while True: + global rere + rere='' + data = fp.read(1024) + if not data: + print('{0} 文件发送完毕...'.format(os.path.basename(filepath))) + txtMsgList.insert(END, '{0} 文件发送完毕...'.format(os.path.basename(filepath)), 'greencolor') + break + print("发送的内容",data) + tosend=fileEncrypt(data) #加密 + s.send(str(len(tosend)).encode('utf-8')) + s.send(tosend) + while True: + if s.recv(1024).decode('utf-8')=='I have receive the past one': + break + # 关闭当期的套接字对象 + s.close() + +# 主页 +def mainPage(): #client客户端 + def sendMsg(Sock): # 发送消息 + strMsg = "我:" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + '\n' + txtMsgList.insert(END, strMsg, 'greencolor') + Mes = txtMsg.get('0.0', END) + txtMsgList.insert(END, Mes) + onceKey = AESalgorithm.genKey() # 一次一密 密钥 + digest = RSAalgorithm.RsaSignal(Mes, CLIENTPRIVATE) # 先hash再签名# 生成消息摘要 + message = {'Message': Mes, 'digest': digest.decode("utf-8")} # 把消息和摘要打包 + message = json.dumps(message) # 转成json字符串 + message = AESalgorithm.AesEncrypt(message, onceKey) # 合并加密 + encrykey = RSAalgorithm.RsaEncrypt(onceKey, SERVERPUBLIC) # 用服务器公钥加密一次密钥 + txtMsg.delete('0.0', END) + Message = pickle.dumps([message, encrykey.decode('utf-8')]) # 序列化消息,用于传输 + Sock.send(Message) + def RecvMsg(Sock, test): # 接受消息函数 + global SERVERPUBLICs + while True: + Message = Sock.recv(BUFF) # 收到文件 + (message, encrykey) = pickle.loads(Message) + + mykey = RSAalgorithm.RsaDecrypt(encrykey, CLIENTPRIVATE) # 用私钥解密获得一次密钥 + print('mykey', mykey.decode('utf-8')) + decryMes = AESalgorithm.AesDecrypt(message, mykey.decode('utf-8')) # 用一次密钥解密消息,获得包含消息内容和摘要的json + decryMes = json.loads(decryMes) # 将json转换为python字典 + content = decryMes['Message'] + digest = decryMes['digest'].encode('utf-8') + + if RSAalgorithm.VerRsaSignal(content, digest, SERVERPUBLIC): + strMsg = "对方:" + time.strftime("%Y-%m-%d %H:%M:%S", + time.localtime()) + "通过数字签名认证,本次密钥为" + mykey.decode('utf-8') + '\n' + txtMsgList.insert(END, strMsg, 'greencolor') + txtMsgList.insert(END, content + '\n') + + def cancelMsg(): # 清空消息内容 + txtMsg.delete('0.0', END) + + def sendMsgEvent(event, Sock): # 发送消息事件 + if event.keysym == 'Up': + sendMsg(Sock) + + def UploadAction(event=None): # 上传文件 + filename = filedialog.askopenfilename() + print('Selected:', filename) + initSendSocket(filename) + + def exchangePublicKey(dir):#交换密钥 + global ClientSock, txtMsgList + with open(dir, 'rb') as fi: + publicKey = fi.read() + has = hashalg.hash_sha256(publicKey) + Message = pickle.dumps([publicKey, has]) + try: + ClientSock.send(Message) + txtMsgList.insert(END, "发送公钥成功\n") + except: + txtMsgList.insert(END, "密钥发送失败,正在尝试重新发送...\n") + exchangePublicKey(dir) + + def verifyKey(Sock):#验证公钥 + global txtMsgList, SERVERPUBLIC + while True: + Message = Sock.recv(BUFF) + (publickey, hash_256) = pickle.loads(Message) + if hash_256 == hashalg.hash_sha256(publickey): + txtMsgList.insert(END, "公钥完整性验证完成,可以开始传输文件\n") + SERVERPUBLIC = publickey + txtMsgList.insert(END, "收到公钥\n" + SERVERPUBLIC.decode('utf-8') + "\n") + break + else: + txtMsgList.insert(END, "验证失败\n") + def cnct(): # 连接操作 + global txtMsgList, ClientSock + ClientSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ServerAddr = IP + ClientSock.connect((ServerAddr, PORT)) + print('连接成功,可以开始传输消息和文件了\n') + txtMsgList.insert(END, "连接成功,可以开始传输消息和文件了" + IP + ":" + str(PORT) + "\n") + exchangePublicKey("keys/client/clientpublic.pem") # 发送公钥 + verifyKey(ClientSock) # 验证对方密钥 + thread = threading.Thread(target=RecvMsg, args=(ClientSock, None)) + thread.start() + return ClientSock + def setIpWindows(): # 设置ip的子窗口 + def setNewIP(newip, newport): + global txtMsgList + print(newip, newport) + global IP + IP = str(newip) + global PORT + PORT = int(newport) + set.destroy() + try: + cnct() + except: + txtMsgList.insert(END, "连接异常,ip或端口不可访问") + tkinter.messagebox.showwarning('连接失败', '连接异常,ip或端口不可访问\n') + print("连接异常,ip或端口不可访问\n") + + set = Tk() + set.title('设置ip地址和端口号') + set.geometry('350x200') + set.resizable(0, 0) + # ip + Label(set, text='IP地址:').place(x=10, y=10) + ent1 = Entry(set) + ent1.place(x=150, y=10) + # port + Label(set, text='端口号:').place(x=10, y=50) + ent2 = Entry(set) + ent2.place(x=150, y=50) + bt_connect = Button(set, text='连接', command=lambda: setNewIP(ent1.get(), ent2.get())) + bt_connect.place(x=150, y=130) + set.mainloop() + def start(): + # 以下是生成聊天窗口的代码 + def selectEven(*args): + print(selal.get()) + global app, frmLT, frmLC, frmLB, txtMsgList, txtMsg, btnSend, btnCancel, btnFile, btnSet + # 创建窗口 + app = Tk() + app.title('网络加密软件-Client') + app.resizable(0, 0) + + # 创建frame容器 + frmLT = Frame(width=500, height=320, bg='white') + frmLC = Frame(width=500, height=150, bg='white') + frmLB = Frame(width=500, height=30) + # frmRT = Frame(width = 200, height = 500) + + # 创建控件 + txtMsgList = Text(frmLT) + txtMsgList.tag_config('greencolor', foreground='#008C00') # 创建tag + txtMsg = Text(frmLC) + txtMsg.bind("", sendMsgEvent) + selal = StringVar() + btnSend = Button(frmLB, text='发送', width=8, command=lambda: sendMsg(ClientSocket)) + btnCancel = Button(frmLB, text='取消', width=8, command=cancelMsg) + btnFile = Button(frmLB, text='上传文件', width=8, command=UploadAction) + btnSet = Button(frmLB, text='设置ip', width=8, command=setIpWindows) + btnAlSel = ttk.Combobox(frmLB, textvariable=selal, state='readonly') + btnAlSel['values'] = ('AES-CBC', '待定2') + btnAlSel.current(0) + btnAlSel.bind("<>", selectEven) + print("selal is ", selal.get()) + # btnFile.pack() + # imgInfo = PhotoImage(file = "timg-2.gif") + # lblImage = Label(frmRT, image = imgInfo) + # lblImage.image = imgInfo + + # 窗口布局 + frmLT.grid(row=0, column=0, columnspan=2, padx=1, pady=3) + frmLC.grid(row=1, column=0, columnspan=2, padx=1, pady=3) + frmLB.grid(row=2, column=0, columnspan=2) + # frmRT.grid(row = 0, column = 2, rowspan = 3, padx =2, pady = 3) + + # 固定大小 + frmLT.grid_propagate(0) + frmLC.grid_propagate(0) + frmLB.grid_propagate(0) + # frmRT.grid_propagate(0) + + btnSend.grid(row=2, column=0) + btnCancel.grid(row=2, column=1) + btnFile.grid(row=2, column=2) + btnSet.grid(row=2, column=3) + btnAlSel.grid(row=2, column=4) + # lblImage.grid() + txtMsgList.grid() + txtMsg.grid() + # 主事件循环 + app.mainloop() + + thread_gui = threading.Thread(target=start) + thread_gui.start() + + ClientSocket = cnct() #返回客户端套接字 + # try: + # ClientSocket=cnct() + # except: + # addSysTip("连接异常,ip或端口不可访问") + # tkinter.messagebox.showwarning('连接失败', '连接异常,ip或端口不可访问,点击设置按钮重新设置\n') + # print("连接异常,ip或端口不可访问\n") + + +def main(): + initKey() + mainPage() + + +if __name__ == "__main__": + main()