此篇接上篇,有兴趣的可以去主页或专栏看看,没有的话咱直接开始:
基于RSA加密和Tkinter可视化的密码存储程序(可用于期末作业设计、Python练习、实用应用;抗错误输入、抗密码盗取)二:登录、注册界面_山河之书Liu_Zixin的博客-CSDN博客这个就算了吧)但是,密码是8位以上的无限位,同时需要包含大小写字母和数字,让暴力破解当场崩溃,接下来就是代码段(注释明确),我会在最后放上程序截图。注册界面(为保证安全,仅支持一次性注册),密码修改界面,密保问题修改界面,忘记密码登录界面;密码信息(平台,账号,密码三个属性)插入,基于平台名称的密码信息模糊查询,密码信息定位修改(仅能修改密码,其他两项不能),密码信息定位删除,用户密码登录,忘记密码登录,登录密码修改,密保问题修改,密钥(公钥私钥)加密存储,密码信息、登录检查信息加密存储;https://blog.csdn.net/weixin_61864411/article/details/127177297?spm=1001.2014.3001.5501 本文主要包括数据操纵主界面(增删改查功能一个不少),还有修改密码,修改密保问题及答案的辅助界面,主要逻辑就是读出加密存储的信息列表,然后私钥解密,进行基于关键字的查询或者执行基于关键字删除,修改;或者是一次性插入。完成后对写入的新信息进行加密,写入列表,重新写入文件;如果仅查询,则仅读取而不写入。
修改密码和密保问题、答案的地方也是同一道理,读取出登录信息列表,然后再将替换的信息加密写回,将列表写回原文件即可。
话不多说上代码,代码包含详细注释,希望能帮到任何看到的人,有些注释在文章二中反复注释过了,就可能不再注释了。程序截图将放在代码段之后。
# 别问模块调用去哪里了,问就是,在上一篇文章里,按理说,这段是插在上一篇文章的代码的设置最后几个log_in等按键之前的,就是那里,嗯,对的,这一段代码是无法单独直接运行的。
def main():
window_main = tk.Tk() # 数据操纵主界面
window_main.title("TOP SECRET")
window_main.geometry("600x500")
tree = ttk.Treeview(window_main) # 这就是展示表啦
tree["column"] = ("platform", "user_name", "user_password") # 展示表的展示属性
tree.column("platform", width=132) # 设置platform属性框宽度
tree.heading("platform", text="PLATFORM") #设置platform属性框名
tree.column("user_name", width=132) # 同上之理
tree.heading("user_name", text="USERNAME")
tree.column("user_password", width=132)
tree.heading("user_password", text="PASSWORD")
tree.place(x=0, y=100) # 设置属性框位置
platform = tk.Label(window_main, text="PLATFORM-KEYWORD") # 设置关键字“平台”的输入框,详细注释见上一篇文章
platform.grid(row=1, column=0)
platform_str = tk.StringVar()
platform_input = tk.Entry(window_main, width=20, textvariable=platform_str)
platform_input.grid(row=1, column=1)
user_name = tk.Label(window_main, text="USERNAME") # 设置要存储的用户名输入框
user_name.grid(row=2, column=0)
user_name_str = tk.StringVar()
user_name_input = tk.Entry(window_main, width=20, textvariable=user_name_str)
user_name_input.grid(row=2, column=1)
user_password = tk.Label(window_main, text="PASSWORD") # 设置要存储的密码输入框
user_password.grid(row=3, column=0)
user_password_str = tk.StringVar()
user_password_input = tk.Entry(window_main, width=20, textvariable=user_password_str)
user_password_input.grid(row=3, column=1)
def select(): # 查找密码
try:
with open("file_password.pickle", "rb+") as file_password: # 读取密码存储文件,注意此处是rb+模式打开,因此在遇到没有文件的时候就会报错,配合后面的故障排除,完美
password_data = pickle.load(file_password) # 获取密码
with open("private.pem") as private_file:
q = private_file.read() # 获取私钥,毕竟只需要读取不需要写入,公钥就不需要了
privkey_use = rsa.PrivateKey.load_pkcs1(q.encode()) # 解码出私钥
platform_info = platform_input.get() # 获取关键词(平台名称)
pattern = r"^\w*\s*\w*\s*\w*\s*(%s)\s*\w*\s*\w*\s*\w*$" % platform_info # 正则表达式,用于模糊匹配检查,这里之所以折腾这一大段,是因为不连续空格无法一次性识别的问题,我设置了最多可以有完全不连续的四个空格(前后各四个),想必可以搞定大部分使用场景吧。
list_show = [] # 原来的列表里面的数据是加密的,需要先解密,然后写入新的列表
if len(platform_info) == 0: # 没有信息的情况下
for i in range(len(password_data)):
list_element = []
for j in range(len(password_data[i])):
data_j_0 = rsa.decrypt(password_data[i][j], privkey_use)
data_j = data_j_0.decode()
list_element.append(data_j)
list_show.append(list_element) # 全部解码,写入新的列表,准备输出
else: # 如果有输入信息
for i in range(len(password_data)):
list_element = []
data_0 = rsa.decrypt(password_data[i][0], privkey_use) # 此处先解码出关键字平台,先进行匹配,成了再解密其他信息,提高效率的
data = data_0.decode()
tf = re.match(pattern, data) # 进行模糊匹配检查,如果符合一并输出
if tf:
for j in range(len(password_data[i])):
data_j_0 = rsa.decrypt(password_data[i][j], privkey_use) # 解密其他信息
data_j = data_j_0.decode()
list_element.append(data_j)
list_show.append(list_element)
else:
continue # 不符合直接跳过,然后最后如果没有列表自动为空,啥也检索不出
list_show.sort(key=lambda ele: ele[0], reverse=False) # 排序,这个……就不再自选什么升序降序了,直接默认吧,FALSE是啥我也忘了,可能是升序,大家自己试试就知道了
x = tree.get_children() # 获取信息总元组(行)数
for i in x:
tree.delete(i) # 这还原初始化的
for i in range(len(list_show)):
tree.insert("", i, text=str(i + 1), values=(list_show[i][0], list_show[i][1], list_show[i][2])) # 插入信息,那个text之所以要+1,是因为众所周知,列表索引是从0开始的
file_password.close()
private_file.close() # 关文件
except FileNotFoundError: # 没有文件就先去插入一条,顺百年间文件
tk.messagebox.showinfo("", "No data. Please first insert one.")
def insert():
try:
platform_info = platform_input.get()
user_name_info = user_name_input.get()
user_password_info = user_password_input.get()
with open("file_password.pickle", "wb+") as file_password:
password_data = pickle.load(file_password) # 这里就是插入的重要意义,wb+可以在没有文件的时候新建
with open("public.pem") as public_file:
p = public_file.read()
pubkey_use = rsa.PublicKey.load_pkcs1(p.encode()) # 这个不需要读取,只需要写入,只需要公钥
if platform_info != 0 and user_name_info != 0 and user_password_info != 0: # 数据不能为0
platform_info_encrypted = rsa.encrypt(platform_info.encode(), pubkey_use)
user_name_info_encrypted = rsa.encrypt(user_name_info.encode(), pubkey_use)
user_password_info_encrypted = rsa.encrypt(user_password_info.encode(), pubkey_use)
list_element = [platform_info_encrypted, user_name_info_encrypted, user_password_info_encrypted] # 常规操作,详参上一篇注册函数插入登录信息
password_data.append(list_element)
pickle.dump(password_data, file_password)
tk.messagebox.showinfo("CORRECT", "INSERT SUCCESSFULLY")
else: # 但凡有一个数据缺失,都不让插入
tk.messagebox.showinfo("", "Please input all information needed.")
file_password.close()
public_file.close()
except EOFError: # 这是为了防止有文件,但文件为空的情况,即新建文件时是无法从中读取出列表的,此时报EOFError,自动转到这里,插入一个新的初始列表,就好了,上面那个只能用于第二次插入(就算第一次插入后把数据删除了,但是预设列表在那里,就足够了)
platform_info = platform_input.get()
user_name_info = user_name_input.get()
user_password_info = user_password_input.get()
with open("public.pem") as public_file:
q = public_file.read()
file_password = open("file_password.pickle", "wb+")
pubkey_use = rsa.PublicKey.load_pkcs1(q.encode())
password_data = []
if platform_info != 0 and user_name_info != 0 and user_password_info != 0:
platform_info_encrypted = rsa.encrypt(platform_info.encode(), pubkey_use)
user_name_info_encrypted = rsa.encrypt(user_name_info.encode(), pubkey_use)
user_password_info_encrypted = rsa.encrypt(user_password_info.encode(), pubkey_use)
list_element = [platform_info_encrypted, user_name_info_encrypted, user_password_info_encrypted]
password_data.append(list_element)
pickle.dump(password_data, file_password)
tk.messagebox.showinfo("CORRECT", "INSERT SUCCESSFULLY")
else:
tk.messagebox.showinfo("", "Please input all information needed.")
file_password.close()
public_file.close()
def change(): # 修改数据用的函数
try:
with open("file_password.pickle", "rb+") as file_password:
password_data = pickle.load(file_password)
with open("private.pem") as private_file:
q = private_file.read()
with open("public.pem") as public_file: # 这次可是得公私钥都读出了,毕竟要写还要根据修改目标进行筛选
p = public_file.read()
privkey_use = rsa.PrivateKey.load_pkcs1(q.encode())
pubkey_use = rsa.PublicKey.load_pkcs1(p.encode())
platform_info = platform_input.get()
user_password_info = user_password_input.get()
list_item = []
num = len(password_data)
for i in range(num):
data_i_0 = rsa.decrypt(password_data[i][0], privkey_use) #循环读取解密关键词平台
data_i = data_i_0.decode()
list_item.append(data_i)
if platform_info in list_item: # 如果出现关键词和输入的一样(这个必须完全一样)
for i in range(num):
if list_item[i] == platform_info:
user_password_info_encrypted = rsa.encrypt(user_password_info.encode(), pubkey_use)
password_data[i][2] = user_password_info_encrypted # 修改密码
with open("file_password.pickle", "rb+") as file_password:
pickle.dump(password_data, file_password) # 重新写入
tk.messagebox.showinfo("CORRECT", "CHANGE SUCCESSFULLY")
break
else:
continue
else: # 找不到的话就提示
tk.messagebox.showinfo("", "No such platform. Please confirm before type.")
file_password.close()
private_file.close()
public_file.close()
except FileNotFoundError: # 这操作也得有数据才能操作,是吧?
tk.messagebox.showinfo("", "No data. Please first insert one.")
def delete(): # 删除函数,使用的和change几乎一样的逻辑,只是不再替换而是直接删掉这一个二维列表中的一个列表项,这段代码作为对上一段代码的理解代码,看懂上一个之后来看这个练练手
try:
with open("file_password.pickle", "rb+") as file_password:
password_data = pickle.load(file_password)
with open("private.pem") as private_file:
q = private_file.read()
privkey_use = rsa.PrivateKey.load_pkcs1(q.encode())
platform_info = platform_input.get()
list_item = []
num = len(password_data)
for i in range(num):
data_i_0 = rsa.decrypt(password_data[i][0], privkey_use)
data_i = data_i_0.decode()
list_item.append(data_i)
if platform_info in list_item:
for i in range(num):
if list_item[i] == platform_info:
del password_data[i]
with open("file_password.pickle", "rb+") as file_password:
pickle.dump(password_data, file_password)
tk.messagebox.showinfo("CORRECT", "DELETE SUCCESSFULLY")
file_password.close()
private_file.close()
break
else:
continue
else:
tk.messagebox.showinfo("", "No such platform. Please confirm before type.")
except FileNotFoundError:
tk.messagebox.showinfo("", "No data. Please first insert one.")
def quit_window(): # 退出该界面
window_main.destroy()
tk.Button(window_main, text="SELECT", command=select).place(x=50, y=350)
tk.Button(window_main, text="INSERT", command=insert).place(x=150, y=350)
tk.Button(window_main, text="CHANGE", command=change).place(x=250, y=350)
tk.Button(window_main, text="DELETE", command=delete).place(x=350, y=350)
tk.Button(window_main, text="QUIT", command=quit_window).place(x=488, y=350)
tk.Button(window_main, text="CHANGE PASSWORD", command=change_password).place(x=50, y=390)
tk.Button(window_main, text="CHANGE QUESTION", command=change_question).place(x=400, y=390) # 六个按键自然不必多说
def change_password(): # 修改密码函数
def change_password_use():
with open("file_log_in.pickle", "rb+") as file_log_in:
data_log_in = pickle.load(file_log_in)
with open("public.pem") as public_file:
p = public_file.read()
pubkey_use = rsa.PublicKey.load_pkcs1(p.encode())
password_change_info = password_change_input.get()
password_change_confirm_info = password_change_confirm_input.get()
pattern_password = r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[0-9a-zA-Z]{8,12}$"
result_password = re.match(pattern_password, password_change_info)
if password_change_info == 0 or password_change_confirm_info == 0:
tk.messagebox.showinfo("", "Information can not be empty")
elif not result_password:
tk.messagebox.showinfo("", "Password must contain more than 8 characters, \n"
"including numbers, upper and lower case letters.")
elif password_change_info != password_change_confirm_info:
tk.messagebox.showinfo("", "Inconsistent passwords.")
else:
password_change_info_encrypted = rsa.encrypt(password_change_info.encode(), pubkey_use)
password_change_confirm_info_encrypted = rsa.encrypt(password_change_confirm_info.encode(), pubkey_use)
data_log_in[1] = password_change_info_encrypted
data_log_in[2] = password_change_confirm_info_encrypted
with open("file_log_in.pickle", "wb+") as file_log_in:
pickle.dump(data_log_in, file_log_in)
file_log_in.close()
public_file.close()
tk.messagebox.showinfo("CORRECT", "CHANGE SUCCESSFULLY")
window_change_password.destroy()
window_change_password = tk.Tk()
window_change_password.title("CHANGE PASSWORD")
window_change_password.geometry("230x150")
password_change = tk.Label(window_change_password, text="Please input new password.")
password_change.place(x=10, y=20)
password_change_str = tk.StringVar()
password_change_input = tk.Entry(window_change_password, width=30, textvariable=password_change_str, show="*")
password_change_input.place(x=10, y=40)
password_change_confirm = tk.Label(window_change_password, text="Please input new password again.")
password_change_confirm.place(x=10, y=70)
password_change_confirm_str = tk.StringVar()
password_change_confirm_input = tk.Entry(window_change_password, width=30, textvariable=password_change_confirm_str,
show="*")
password_change_confirm_input.place(x=10, y=90)
change = tk.Button(window_change_password, text="CHANGE", command=change_password_use)
change.place(x=80, y=120)
def change_question(): # 修改密保问题及答案函数
def change_question_use():
with open("file_log_in.pickle", "rb+") as file_log_in:
data_log_in = pickle.load(file_log_in)
with open("public.pem") as public_file:
p = public_file.read()
pubkey_use = rsa.PublicKey.load_pkcs1(p.encode())
security_question_change_info = security_question_change_input.get()
answer_change_info = answer_change_input.get()
if security_question_change_info == 0 or answer_change_info == 0:
tk.messagebox.showinfo("", "Information can not be empty")
else:
security_question_change_info_encrypted = rsa.encrypt(security_question_change_info.encode(), pubkey_use)
answer_change_info_encrypted = rsa.encrypt(answer_change_info.encode(), pubkey_use)
data_log_in[3] = security_question_change_info_encrypted
data_log_in[4] = answer_change_info_encrypted
with open("file_log_in.pickle", "wb+") as file_log_in:
pickle.dump(data_log_in, file_log_in)
file_log_in.close()
public_file.close()
tk.messagebox.showinfo("CORRECT", "CHANGE SUCCESSFULLY")
window_change_question.destroy()
window_change_question = tk.Tk()
window_change_question.title("CHANGE PASSWORD")
window_change_question.geometry("230x150")
security_question_change = tk.Label(window_change_question, text="Please input new security question.")
security_question_change.place(x=10, y=20)
security_question_change_str = tk.StringVar()
security_question_change_input = tk.Entry(window_change_question, width=30,
textvariable=security_question_change_str, show="*")
security_question_change_input.place(x=10, y=40)
answer_change = tk.Label(window_change_question, text="Please input answer.")
answer_change.place(x=10, y=70)
answer_change_str = tk.StringVar()
answer_change_input = tk.Entry(window_change_question, width=30, textvariable=answer_change_str, show="*")
answer_change_input.place(x=10, y=90)
change = tk.Button(window_change_question, text="CHANGE", command=change_question_use)
change.place(x=80, y=120)
程序静态效果图:
有些晚了,截图草率了点,见谅,我都测试过的,该有的功能一个不少