import socket import sys import threading import time import pickle from io import StringIO import CoreAlgorithm import DataSearch # 审批回执类消息head:01001001 # sql密文类消息head:01001010 # 元数据类消息head:01001100 # 实现GUI界面 import tkinter as tk from tkinter import * from tkinter import messagebox, scrolledtext from PIL import ImageTk # 美化 from ttkbootstrap import Style # pylint: disable=e0401 # type: ignore from tkinter import ttk sleep_time = 1 # 用于生成本地公钥和私钥 def generate_key_pair_data(): public_key_data, private_key_data = CoreAlgorithm.generate_paillier_keypair() return private_key_data, public_key_data # 用公钥为本地生成数字证书 def get_certificate(temp_public_key, cert_name): public_key_str = "\n".join( temp_public_key[i : i + 60] for i in range(0, len(temp_public_key), 60) ) pack_public_key = ( "-----BEGIN PUBLIC KEY-----\n" + public_key_str + "\n-----END PUBLIC KEY-----" ) cert = {"public_key": pack_public_key, "name": "<数据提供方>" + cert_name} return cert # 公钥转公钥信息 def key_to_data(key): # str转bytes byte = bytes.fromhex(key) # bytes转PaillierPublicKey data = pickle.loads(byte) return data # 加密字符串 def str_to_encrypt(message, public_data): # str 转 int if message.isdigit(): int_message = int(message) else: int_message = int.from_bytes(message.encode(), "big") enc_message = public_data.encrypt(int_message) print("int_message", int_message) return enc_message class MyClient: def __init__(self): # 实现GUI主界面框用到的参数 self.root = None self.data_text = None self.name_text = None self.message_text = None self.window = None # 初始化最近一次的待处理的sql申请信息 self.recent_message = "" # 初始化本端名 self.name = "" # 初始化sql self.sql = "" # 准备界面函数 self.root_window() self.give_name() # 生成私钥和公钥信息 ( self.private_key_data, self.public_key_data, ) = generate_key_pair_data() # PaillierPublicKey 类型 # 生成私钥和公钥字符串 self.private_key, self.public_key = self.generate_key_pair() # 初始化数字证书 self.certificate = {} # 等名字输入了再生成 # 初始化socket self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 初始化根界面 def root_window(self): self.root = tk.Tk() self.root.title("数据提供方智慧医疗SQL查询系统") self.root.geometry("450x540") # 增加背景图片 global photo photo = ImageTk.PhotoImage(file="background.png") label = Label(self.root, image=photo) label.pack() self.create_widgets() # 调用函数创建功能按钮 # 为本端取名 def give_name(self): self.window = tk.Toplevel() self.window.title("命名窗口") self.window.geometry("300x320") # 初始化信息输入框 self.name_text = tk.StringVar() tk.Label(self.window, bg="green", text="请为本端取名:").place(x=50, y=100) input_message = tk.Entry(self.window, textvariable=self.name_text) input_message.pack() input_message.place(x=80, y=150) # 确认按钮及位置 bt_confirm = tk.Button( self.window, bg="green", text="确认", width=10, command=self.get_name ) bt_confirm.pack() bt_confirm.place(x=125, y=220) # 读取输入框的name,保存在self.name中 def get_name(self): self.name = self.name_text.get() messagebox.showinfo("提示", "命名成功!") self.window.destroy() self.root.title("<数据提供方>" + self.name + "的智慧医疗系统") # 安全通信 def safe_connect(self): if self.certificate != {}: # 与服务器端地址建立通信, 8080为服务端程序的端口号 self.client.connect(("localhost", 1111)) # 为接收消息函数添加线程 threading.Thread(target=self.get_message).start() time.sleep(3) else: messagebox.showinfo("提示", "请先生成你的公钥证书!") # 向平台发送本地公钥信息 def send_public_key(self): print("正在向平台发送本地公钥信息...") self.client.send(str(self.certificate).encode()) print(self.certificate) time.sleep(sleep_time) print("发送成功!") # 产生公私钥字符串 def generate_key_pair(self): # Paillier 转 bytes public_key_bytes = pickle.dumps(self.public_key_data) private_key_bytes = pickle.dumps(self.private_key_data) # bytes 转 str public_key = public_key_bytes.hex() private_key = private_key_bytes.hex() return private_key, public_key # 打印公钥 def print_public_key(self): print("本地公钥信息为:") print(self.public_key_data) print("打印公钥如下:", end="") public_key_str = "\n".join( self.public_key[i : i + 60] for i in range(0, len(self.public_key), 60) ) pack_public_key = ( "\n-----BEGIN PUBLIC KEY-----\n" + public_key_str + "\n-----END PUBLIC KEY-----\n" ) print(pack_public_key) messagebox.showinfo("本地公钥", pack_public_key) # GUI界面显示 # 打印私钥 def print_private_key(self): print("本地私钥信息为:") print(self.private_key_data) private_key_str = "\n".join( self.private_key[i : i + 60] for i in range(0, len(self.public_key), 60) ) pack_private_key = ( "-----BEGIN PRIVATE KEY-----\n" + private_key_str + "\n-----END PRIVATE KEY-----\n" ) print("打印私钥如下:", end="") print(pack_private_key) messagebox.showinfo("本地私钥", pack_private_key) # GUI界面显示 # 打印证书 def print_certificate(self): if self.name != "": self.certificate = get_certificate(self.public_key, self.name) print("本地公钥证书为:") message = "" for key, value in self.certificate.items(): print(key, ":\n", value) message += key + ":\n" + value + "\n" messagebox.showinfo("本地公钥证书", message) else: messagebox.showinfo("提示", "请先为平台命名!") # 接收消息 def get_message(self): while True: # 接收连接结果信息 message = self.client.recv(4096).decode() print(message) if message == "安全多方计算平台:单向授权成功!": self.client.send("请发送平台的完整公钥证书".encode()) while True: message = self.client.recv(4096).decode() # 公钥字符串比较长,要多一点 if message.startswith("{'public_key':"): # 等待接收平台公钥证书 cert = eval(message) messagebox.showinfo("连接结果", "与平台连接成功!") print("接收到的平台的公钥证书如下:") message = "" for key, value in cert.items(): if isinstance(value, bytes): value = value.decode() print(key, ":\n", value) message += key + ":\n" + value messagebox.showinfo("平台的公钥证书", message) server_public_key = ( cert["public_key"] .split("-----END PUBLIC KEY-----")[0] .split("-----BEGIN PUBLIC KEY-----")[1] ) server_public_key = server_public_key.replace("\n", "") print("提取到的server_public_key:") print(server_public_key) global server_public_key_data server_public_key_data = key_to_data(server_public_key) print("对应的server_public_key_data:") print(server_public_key_data) messagebox.showinfo("平台的公钥信息", server_public_key_data) self.client.send("认证成功!".encode()) self.send_public_key() # 单向认证后把自己的公钥证书发给平台实现双向认证 break else: print("错误的Message", message) elif message == "安全多方计算平台:双向授权成功!": messagebox.showinfo("平台发来的消息", "与平台的双向认证成功!") self.client.send("请求查询方信息!".encode()) cert = self.client.recv(4096).decode() cert = eval(cert) message_p = "接收到一则非平台的公钥证书" print(message_p) # messagebox.showinfo("提示", message_p) message = "" for key, value in cert.items(): if isinstance(value, bytes): value = value.decode() print(key, ":\n", value) message += key + ":\n" + value + "\n" # messagebox.showinfo("数据查询方的公钥证书", message) search_public_key = ( cert["public_key"] .split("-----END PUBLIC KEY-----")[0] .split("-----BEGIN PUBLIC KEY-----")[1] ) search_public_key = search_public_key.replace("\n", "") print("提取到的search_public_key:") print(search_public_key) global search_public_key_data search_public_key_data = key_to_data(search_public_key) print("对应的search_public_key_data:") print(search_public_key_data) # messagebox.showinfo("数据查询方的公钥信息", search_public_key_data) elif message.startswith("01001010"): message = message.split("01001010", 1)[ 1 ] # [0]是空字符串,已测试。只切一次是防止密文出现和头部一样的信息被误切。 # 使用私钥解密消息,获得sql明文 print("接收到的sql密文:", message) int_message = int(message) # Ciphertext转EncryptedNumber Encrpt_message = CoreAlgorithm.EncryptedNumber( self.public_key_data, int_message, exponent=0 ) dec_int_message = self.private_key_data.decrypt(Encrpt_message) dec_message = dec_int_message.to_bytes( (dec_int_message.bit_length() + 7) // 8, "big" ).decode() print("解密后的消息为:", dec_message) self.sql = dec_message.split("||")[2] print("收到已授权方的sql:", self.sql) self.design_window() # --------------------------接收sql消息的窗口设计函数------------------------- # 接受调用数据的请求 def send_accept_reply(self): self.client.send( ( "01001001" + str(self.client.getsockname()) + "||" + self.name + "同意了你的数据申请。" ).encode() ) self.window.destroy() self.window = tk.Toplevel() self.window.title("命名窗口") self.window.geometry("300x320") # 信息输入框 self.data_text = tk.StringVar() tk.Label(self.window, bg="green", text="请输入要调用的信息:").place(x=50, y=100) input_message = tk.Entry(self.window, textvariable=self.data_text) input_message.pack() input_message.place(x=80, y=150) # 确认按钮及位置 bt_confirm = tk.Button( self.window, bg="green", text="确认", width=10, command=self.send_data ) bt_confirm.pack() bt_confirm.place(x=125, y=220) # 发送要调用的信息 def send_data(self): message = self.data_text.get() print(message, type(message)) # 加密并封装消息 print("正在封装并加密消息...") global search_public_key_data enc_message = str_to_encrypt(message, search_public_key_data).ciphertext() pack_message = "01001100" + str(enc_message) print("正在发送消息给平台...") self.client.send(pack_message.encode()) print("发送成功!") messagebox.showinfo("提示", "发送成功!") self.window.destroy() # 拒绝调用数据的请求 def send_decline_reply(self): self.client.send( ( "01001001" + str(self.client.getsockname()) + "||" + "想得美哦!不给(*^▽^*)" ).encode() ) messagebox.showinfo("提示", "已拒绝!") self.window.destroy() # 稍后处理调用数据的请求 def wait_reply(self): self.window.destroy() # 接收到sql信息的专属窗口 def design_window(self): if self.sql != "": self.window = tk.Toplevel() self.window.title("平台发来的消息") self.window.geometry("550x320") # 设定窗口大小 self.recent_message = "收到已授权方的sql:" + self.sql tk.Label(self.window, bg="green", text=self.recent_message).place( x=50, y=100 ) # 创建接受按钮 accept_button = tk.Button( self.window, text="接受", width=15, command=self.send_accept_reply ) accept_button.place(x=90, y=220) # 创建拒绝按钮 decline_button = tk.Button( self.window, text="拒绝", width=15, command=self.send_decline_reply ) decline_button.place(x=225, y=220) # 创建稍后处理按钮 decline_button = tk.Button( self.window, text="稍后处理", width=15, command=self.wait_reply ) decline_button.place(x=360, y=220) else: messagebox.showinfo("提示", "暂无待处理的信息。") # ----------------------------------------------------------------------- # 调用本地信息库 def search_data(self): output = StringIO() # 用于保存打印信息 sys.stdout = output # 重定向sys.stdout到StringIO print(self.name) DataSearch.main(self.name) sys.stdout = sys.__stdout__ long_text = output.getvalue() print(long_text) # 创建显示窗口 window = tk.Toplevel() # 创建滚动文本部件 roll = scrolledtext.ScrolledText(window, width=200, height=40) # 打印超长字符串 roll.insert(tk.END, long_text) roll.pack() # --------------------------主界面的按键功能绑定-------------------------------- # 界面按键 def create_widgets(self): # 创建按钮和标签等部件并使用ttk和Style进行美化 style = Style(theme="darkly") # 指定样式主题 self.root = style.master button1 = ttk.Button( self.root, text="查看我的公钥", bootstyle="info-outline", command=self.print_public_key, width=30, ) button1.place(x=104, y=310) button2 = ttk.Button( self.root, text="查看我的私钥", bootstyle="info-outline", command=self.print_private_key, width=30, ) button2.place(x=104, y=348) button3 = ttk.Button( self.root, text="生成我的证书", bootstyle="info-outline", command=self.print_certificate, width=30, ) button3.place(x=104, y=383) button4 = ttk.Button( self.root, text="获取平台认证", bootstyle="info-outline", command=self.safe_connect, width=30, ) button4.place(x=104, y=418) button5 = ttk.Button( self.root, text="处理请求信息", bootstyle="info-outline", command=self.design_window, width=30, ) button5.place(x=104, y=453) button6 = ttk.Button( self.root, text="查看医疗记录", bootstyle="info-outline", command=self.search_data, width=30, ) button6.place(x=104, y=488) # GUI界面 def run(self): self.root.mainloop() # ----------------------------------------------------主程序---------------------------------------------------- global server_public_key_data, search_public_key_data, photo # 使用全局变量,否则图片可能会被回收,不显示 # GUI界面 app = MyClient() app.run()