import base64 import struct from tkinter import * import time from tkinter import filedialog import pickle import socket import threading import tkinter.messagebox from algorithms import AESalgorithm, generateKey, RSAalgorithm, hashalg import json import sys # 使用tkinter建立GUI IP = '127.0.0.1' PORT = 4396 BUFF = 5120 FIP='127.0.0.1' FPORT=7932 # CLIENTPUBLICs # SERVERPUBLICs # SERVERPRIVATEs def initKey(): global SERVERPUBLICs, SERVERPRIVATEs (SERVERPRIVATEs, SERVERPUBLICs) = generateKey.generateMyKey("keys/server/server") def fileDecrypt(data): (message,encrykey)=pickle.loads(data) onceKey= RSAalgorithm.RsaDecrypt(encrykey, SERVERPRIVATEs) print("接收到的密钥",onceKey,type(onceKey)) message= AESalgorithm.AesDecrypt(message, onceKey.decode('unicode_escape')) message=pickle.loads(message) content=base64.b64decode(message['Message']) print('传送的内容是',content) digest=message['digest'] if RSAalgorithm.VerRsaSignal(content, digest, CLIENTPUBLICs): return content def initFileListen(): try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定端口为9001 s.bind((FIP, FPORT)) # 设置监听数 s.listen(10) except socket.error as msg: print(msg) sys.exit(1) print('Waiting connection...') while True: # 等待请求并接受(程序会停留在这一旦收到连接请求即开启接受数据的线程) conn, addr = s.accept() # 接收数据 t = threading.Thread(target=deal_data, args=(conn, addr)) t.start() def deal_data(conn, addr): print('Accept new connection from {0}'.format(addr)) txtMsgList.insert(END, '文件系统收到一个新的连接,地址来自 {0}'.format(addr), 'greencolor') # conn.settimeout(500) # 收到请求后的回复 conn.send('你好,连接建立成功了'.encode('utf-8')) while True: # 申请相同大小的空间存放发送过来的文件名与文件大小信息 fileinfo_size = struct.calcsize('128sl') # 接收文件名与文件大小信息 buf = conn.recv(fileinfo_size) # 判断是否接收到文件头信息 if buf: # 获取文件名和文件大小 filename, filesize = struct.unpack('128sl', buf) fn = filename.strip(b'\00') fn = fn.decode() print('file new name is {0}, filesize if {1}'.format(str(fn), filesize)) txtMsgList.insert(END, '收到的文件名字为 {0}, 文件大小为 {1}'.format(str(fn), filesize), 'greencolor') recvd_size = 0 # 定义已接收文件的大小 # 存储在该脚本所在目录下面 fp = open('./' + str(fn), 'wb') print('start receiving...') txtMsgList.insert(END, '开始接受...', 'greencolor') # 将分批次传输的二进制流依次写入到文件 while not recvd_size == filesize: if filesize - recvd_size > 1024: lens=conn.recv(1024).decode('utf-8') lens=int(lens) print('该段发送长度为',lens) data = conn.recv(lens) data=fileDecrypt(data) recvd_size += len(data) else: lens = conn.recv(1024).decode('utf-8') lens = int(lens) print('该段发送长度为', lens) data = conn.recv(lens) data = fileDecrypt(data) recvd_size = filesize conn.send('I have receive the past one'.encode('utf-8')) fp.write(data) fp.close() print('end receive...') txtMsgList.insert(END, '接收完毕...', 'greencolor') # 传输结束断开连接 conn.close() break def mainPage(): 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) print(Mes) onceKey = AESalgorithm.genKey() # 一次一密 密钥 print("oncekey", onceKey) digest = RSAalgorithm.RsaSignal(Mes, SERVERPRIVATEs) # 先hash再签名# 生成消息摘要 message = {'Message': Mes, 'digest': digest.decode("utf-8")} # 把消息和摘要打包 message = json.dumps(message) # 转成json字符串 message = AESalgorithm.AesEncrypt(message, onceKey) # 合并加密 encrykey = RSAalgorithm.RsaEncrypt(onceKey, CLIENTPUBLICs) # 用服务器公钥加密一次密钥 txtMsg.delete('0.0', END) Message = pickle.dumps([message, encrykey.decode('utf-8')]) # 序列化消息,用于传输 Sock.send(Message) def RecvMsg(Sock, test): global CLIENTPUBLICs while True: Message = Sock.recv(BUFF) # 收到文件 (message, encrykey) = pickle.loads(Message) mykey = RSAalgorithm.RsaDecrypt(encrykey, SERVERPRIVATEs) # 用私钥解密获得一次密钥 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, CLIENTPUBLICs): 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) def addSysTip(mes): global txtMsgList txtMsgList.insert(END, "系统消息:" + mes) def exchangePublicKey(dir): global ConSock, txtMsgList with open(dir, 'rb') as fi: publicKey = fi.read() # print(publicKey) has = hashalg.hash_sha256(publicKey) Message = pickle.dumps([publicKey, has]) try: ConSock.send(Message) txtMsgList.insert(END, "发送公钥成功\n") except: txtMsgList.insert(END, "密钥发送失败,正在尝试重新发送...\n") exchangePublicKey(dir) def verifyKey(Sock): global txtMsgList, CLIENTPUBLICs while True: Message = Sock.recv(BUFF) # print("shoudao:",Message) (publickey, hash_256) = pickle.loads(Message) if hash_256 == hashalg.hash_sha256(publickey): txtMsgList.insert(END, "公钥完整性验证完成,可以开始传输文件\n") CLIENTPUBLICs = publickey txtMsgList.insert(END, "收到公钥\n" + CLIENTPUBLICs.decode('utf-8') + "\n") # print("publicc:", CLIENTPUBLICs) break else: txtMsgList.insert(END, "验证失败\n") def cnct(): global txtMsgList, ConSock HOSTIP = '127.0.0.1' ServerSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ServerSock.bind((HOSTIP, PORT)) ServerSock.listen(8) print("本机IP地址为", HOSTIP, "端口号为", PORT, ",正在监听中") txtMsgList.insert(END, "系统消息:" + "本机IP地址为" + HOSTIP + "端口号为" + str(PORT) + ",正在监听中\n") ConSock, addr = ServerSock.accept() print('连接成功') txtMsgList.insert(END, "系统消息:连接成功\n") exchangePublicKey("keys/server/serverpublic.pem") verifyKey(ConSock) thread_rev = threading.Thread(target=RecvMsg, args=(ConSock, None)) thread_rev.start() return ConSock def setIpWindows(): def setNewIP(newip, newport): print(newip, newport) global IP IP = str(newip) global PORT PORT = int(newport) set.destroy() try: cnct() except: addSysTip("连接异常,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(): global app, frmLT, frmLC, frmLB, txtMsgList, txtMsg, btnSend, btnCancel, btnFile, btnSet # 创建窗口 app = Tk() app.title('Server') 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) btnSend = Button(frmLB, text='发送', width=8, command=lambda: sendMsg(ServerSocket)) 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) # 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) # lblImage.grid() txtMsgList.grid() txtMsg.grid() # 主事件循环 app.mainloop() thread_gui = threading.Thread(target=start) thread_gui.start() ServerSocket = cnct() # try: # ServerSocket=cnct() # except: # addSysTip("连接异常,ip或端口不可访问") # tkinter.messagebox.showwarning('连接失败', '连接异常,ip或端口不可访问,点击设置按钮重新设置\n') # print("连接异常,ip或端口不可访问\n") def main(): initKey() thread_1=threading.Thread(target=initFileListen) # thread_2=threading.Thread(target=mainPage) mainPage() thread_1.start() # thread_2.start() if __name__ == "__main__": main()