使用Python3自带GUI、RSA、SQLite3做的密码管理软件 V3.0 证书加密解密密码

#_*_ coding:utf8 _*_

import os,time,random

## pycryptodome
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5

## 数据库
import sqlite3


## 图形化模块
import re
from tkinter import *
from tkinter import filedialog  # 选择文件用
import tkinter.messagebox       # 弹出提示对话框
from tkinter import ttk         # 下拉菜单控件在ttk中


def 初始化配置文件(CONF_PATH):
    TEXT_CONF = ''
    D_CONF = {}
    if os.path.isfile(CONF_PATH):
        try:
            f = open(CONF_PATH, 'r', encoding='utf8')
            lines = f.readlines()
            f.close()
        except Exception as e:
            pass
        else:
            for line in lines:
                data = line.split('##')[0]
                try:
                    K,V = data.split('==')
                except Exception as e:
                    pass
                else:
                    D_CONF[K.strip()] = V.strip()
    
    if 'SV_数据库文件' in D_CONF:
        SV_数据库文件.set(D_CONF['SV_数据库文件'])
    else:
        SV_数据库文件.set('PWD.DB')  # 设置默认值
        TEXT_CONF += 'SV_数据库文件==PWD.DB ## 保存账号密码的数据库文件路径\n'
    
    if 'SV_选择公钥文件' in D_CONF:
        SV_选择公钥文件.set(D_CONF['SV_选择公钥文件'])
    else:
        SV_选择公钥文件.set('公钥.pem')
        TEXT_CONF += 'SV_选择公钥文件==公钥.pem ## 设置公钥默认存放位置为程序同目录下,方便使用\n'
    
    if 'SV_选择私钥文件' in D_CONF:
        SV_选择私钥文件.set(D_CONF['SV_选择私钥文件'])
    else:
        SV_选择私钥文件.set('私钥.pem')
        TEXT_CONF += 'SV_选择私钥文件==私钥.pem ## 设置私钥默认存放位置为程序同目录下,方便使用(不安全,私钥加密后可以提高安全性)\n'
    
    if 'IV_随机密码位数' in D_CONF and D_CONF['IV_随机密码位数'].isdigit():
        IV_随机密码位数.set(int(D_CONF['IV_随机密码位数']))
    else:
        IV_随机密码位数.set(20)
        TEXT_CONF += 'IV_随机密码位数==20 ## 随机生成的密码长度\n'
    
    if 'IV_账号加密保存_勾选框' in D_CONF and D_CONF['IV_账号加密保存_勾选框']=='1':
        IV_账号加密保存_勾选框.set(1)
    else:
        IV_账号加密保存_勾选框.set(0)
        TEXT_CONF += 'IV_账号加密保存_勾选框==0 ## 1:账号部分用私钥加密存储,其他值为否\n'
    
    if 'IV_密码加密保存_勾选框' in D_CONF and D_CONF['IV_密码加密保存_勾选框']=='1':
        IV_密码加密保存_勾选框.set(1)
    else:
        IV_密码加密保存_勾选框.set(0)
        TEXT_CONF += 'IV_密码加密保存_勾选框==1 ## 1:密码部分用私钥加密存储,其他值为否\n'
    
    if 'SV_私钥密码' in D_CONF:
        SV_私钥密码.set(D_CONF['SV_私钥密码'])  ## 私钥密码不推荐写配置文件,不安全
    else:
        TEXT_CONF += 'SV_私钥密码== ## 私钥密码不推荐写配置文件,不安全\n'
    
    if 'SV_随机密码数字字符集' in D_CONF:
        SV_随机密码数字字符集.set(D_CONF['SV_随机密码数字字符集'])
    else:
        SV_随机密码数字字符集.set('01234567890')
        TEXT_CONF += 'SV_随机密码数字字符集==01234567890 ## 随机密码数字组成部分\n'
    
    if 'SV_随机密码大写字母集' in D_CONF:
        SV_随机密码大写字母集.set(D_CONF['SV_随机密码大写字母集'])
    else:
        SV_随机密码大写字母集.set('ABCDEFGHJKMNPQRSTUVWXYZ')
        TEXT_CONF += 'SV_随机密码大写字母集==ABCDEFGHJKMNPQRSTUVWXYZ ## 随机密码大写字母组成部分\n'
    
    if 'SV_随机密码小写字母集' in D_CONF:
        SV_随机密码小写字母集.set(D_CONF['SV_随机密码小写字母集'])
    else:
        SV_随机密码小写字母集.set('abcdefghjkmnpqrstuvwxyz')
        TEXT_CONF += 'SV_随机密码小写字母集==abcdefghjkmnpqrstuvwxyz ## 随机密码小写字母组成部分\n'
    
    if 'SV_随机密码符号字符集' in D_CONF:
        SV_随机密码符号字符集.set(D_CONF['SV_随机密码符号字符集'])
    else:
        SV_随机密码符号字符集.set('`~!@#$%^&*()-_=+[{]}|;:<.>/?')
        TEXT_CONF += 'SV_随机密码符号字符集==`~!@#$%^&*()-_=+[{]}|;:<.>/? ## 随机密码符号组成部分\n'
    
    ## 配置文件不存在就生成一个模板文件
    if not os.path.exists(CONF_PATH):
        with open(CONF_PATH, 'w', encoding='utf-8') as f:
            f.write(TEXT_CONF)

####################
## TKinter 主窗口 ##
####################
top = Tk()  # 初始化Tk()
top.title('密码管理工具(Python3/tkinterGUI)')   # 设置标题
窗口宽 = 783
窗口高 = 483
# 获取屏幕尺寸以计算布局参数,使窗口居屏幕中央
屏幕宽 = top.winfo_screenwidth()
屏幕高 = top.winfo_screenheight()
主窗口显示位置 = '%dx%d+%d+%d' % (窗口宽, 窗口高, (屏幕宽-窗口宽)/2, (屏幕高-窗口高)/2)
top.geometry(主窗口显示位置)
top.resizable(width=True, height=True) # 设置窗口是否可变长、宽(True:可变,False:不可变)
top.grid_columnconfigure(0, weight=1)   # (第1列, 自适应窗口宽)
top.grid_rowconfigure(1, weight=1)      # (第2行, 自适应窗口高)


## 全局变量 设置 显示
D_CACHE = {'右键菜单对应数据':'', 'LOG':''}
序号宽 = 5
备注宽 = 35
账号宽 = 25
密码宽 = 25
SV_数据库文件 = StringVar()
SV_数据库文件提示 = StringVar()
SV_数据库文件提示.set('Sqlite3')
IV_密钥长度 = IntVar()
SV_新私钥密码 = StringVar()
SV_选择公钥文件 = StringVar()       ## TK全局变量:可以实时更新显示
SV_公钥提示信息 = StringVar()       ## 显示公钥文件的情况
SV_选择私钥文件 = StringVar()
SV_私钥提示信息 = StringVar()
IV_随机密码位数 = IntVar()
SV_随机密码数字字符集 = StringVar()
SV_随机密码大写字母集 = StringVar()
SV_随机密码小写字母集 = StringVar()
SV_随机密码符号字符集 = StringVar()
IV_账号加密保存_勾选框 = IntVar()
IV_密码加密保存_勾选框 = IntVar()
SV_私钥密码 = StringVar()   # 解密用
SV_剪贴板提示信息 = StringVar()

初始化配置文件('PWD.conf')

def 生成新密钥对():
    时间 = time.strftime('%Y%m%d_%H%M%S')
    当前设置密钥长度 = IV_密钥长度.get()
    随机数生成器 = Random.new().read
    密钥对 = RSA.generate(当前设置密钥长度, 随机数生成器)
    
    ## 保存公钥
    if SV_选择公钥文件.get() != '':
        if os.path.exists(SV_选择公钥文件.get()):
            新公钥文件名 = f'公钥_{时间}.pem'
        else:
            新公钥文件名 = SV_选择公钥文件.get()
    else:
        if os.path.exists('公钥.pem'):
            新公钥文件名 = f'公钥_{时间}.pem'
        else:
            新公钥文件名 = '公钥.pem'
    公钥 = 密钥对.publickey().exportKey()                        # 导出公钥
    with open(新公钥文件名, 'wb') as f:
        f.write(公钥)
    LOGGING(f"新公钥文件名={新公钥文件名}", 'i')
    
    ## 保存私钥
    if SV_选择私钥文件.get() != '':
        if os.path.exists(SV_选择私钥文件.get()):
            新私钥文件名 = f'私钥_{时间}.pem'
        else:
            新私钥文件名 = SV_选择私钥文件.get()
    else:
        if os.path.exists('私钥.pem'):
            新私钥文件名 = f'私钥_{时间}.pem'
        else:
            新私钥文件名 = '私钥.pem'
    
    私钥密码 = SV_新私钥密码.get().strip()
    if 私钥密码 == '':
        私钥 = 密钥对.exportKey()                                    # 导出明文私钥
        with open(新私钥文件名, 'wb') as f:
            f.write(私钥)
        LOGGING(f"新私钥文件名(明文私钥)={新私钥文件名}", 'w')
    else:
        私钥 = 密钥对.exportKey(passphrase=私钥密码, pkcs=8, protection='scryptAndAES128-CBC')   # 导出密文私钥
        with open(新私钥文件名, 'wb') as f:
            f.write(私钥)
        LOGGING(f"新私钥文件名(密文私钥)={新私钥文件名}", 'i')
        
    INFO = '生成新密钥对完成,长度为:' + str(当前设置密钥长度)
    tkinter.messagebox.showinfo(title='INFO', message=INFO)
    ## 自动替换为新生成密钥对
    SV_选择公钥文件.set(新公钥文件名)
    SV_公钥提示信息.set('自动替换为新生成的公钥文件')
    SV_选择私钥文件.set(新私钥文件名)
    SV_私钥提示信息.set('自动替换为新生成的私钥文件')

## 显示日志
def LOGGING(TEXT, LEVEL):
    if D_CACHE['LOG'] != '' and D_CACHE['LOG'].winfo_exists()==1:
        D_CACHE['LOG'].insert(0.0, f"{time.strftime('%Y-%m-%d %H:%M:%S')} {TEXT}\n", LEVEL)

def DEF_按钮_选择数据库文件():
    数据库文件路径 = filedialog.askopenfilename()
    SV_数据库文件.set(数据库文件路径)       # 及时更新

def DEF_按钮_选择公钥文件():
    公钥文件路径 = filedialog.askopenfilename()
    SV_选择公钥文件.set(公钥文件路径)

def DEF_按钮_选择私钥文件():
    私钥文件路径 = filedialog.askopenfilename()
    SV_选择私钥文件.set(私钥文件路径)

def DEF_显示设置小窗():
    新窗口 = Toplevel()
    新窗口.title('设置')
    显示坐标 = '+500+200'
    新窗口.geometry(显示坐标)
    新窗口.grid_columnconfigure(0, weight=1)   # (第0列, 自适应窗口宽)
    新窗口.grid_rowconfigure(0, weight=1)      # (第0列, 自适应窗口高)
    
    全局变量展示框 = LabelFrame(新窗口, text='设置 全局变量')   # , bg='#FFD700'
    全局变量展示框.grid(row=0,column=0,sticky='NSEW')
    全局变量展示框.grid_columnconfigure(1, weight=1)
    全局变量展示框.grid_rowconfigure(6, weight=1)
    
    全局变量显示宽 = 35
    
    Button(全局变量展示框, text='选择[数据文件]', command=DEF_按钮_选择数据库文件).grid(row=0,column=0,sticky='NSEW')
    Entry(全局变量展示框, textvariable=SV_数据库文件, width=全局变量显示宽       ).grid(row=0,column=1,sticky='NSEW')
    Label(全局变量展示框, textvariable=SV_数据库文件提示                         ).grid(row=0,column=2,sticky='NSEW')
    
    Label(全局变量展示框, text='设置[密钥长度]'                                  ).grid(row=1,column=0,sticky='NSEW')
    Label(全局变量展示框, textvariable=IV_密钥长度                               ).grid(row=1,column=2,sticky='NSEW')
    
    def 选择密钥长度后执行函数(event):
        设置新密钥长度 = Combobox_密钥长度.get()
        IV_密钥长度.set(设置新密钥长度)
        LOGGING(f"设置新密钥长度={设置新密钥长度}", 'd')
    
    Combobox_密钥长度 = ttk.Combobox(全局变量展示框, width=5)
    Combobox_密钥长度['value'] = (1024, 2048, 3072, 4096)
    Combobox_密钥长度.current(3)                            # 默认值中的内容为索引,从0开始
    IV_密钥长度.set(Combobox_密钥长度.get())
    Combobox_密钥长度.grid(row=1, column=1, sticky='NSEW')
    Combobox_密钥长度.bind('<<ComboboxSelected>>', 选择密钥长度后执行函数)
    
    Label(全局变量展示框, text='设置[私钥密码]'                                  ).grid(row=2,column=0,sticky='NSEW')
    Entry(全局变量展示框, textvariable=SV_新私钥密码, width=全局变量显示宽       ).grid(row=2,column=1,sticky='NSEW')
    Button(全局变量展示框, text='生成新密钥对', command=生成新密钥对             ).grid(row=2,column=2,sticky='NSEW')
    
    Button(全局变量展示框, text='选择[公钥文件]', command=DEF_按钮_选择公钥文件  ).grid(row=3,column=0,sticky='NSEW')
    Entry(全局变量展示框, textvariable=SV_选择公钥文件, width=全局变量显示宽     ).grid(row=3,column=1,sticky='NSEW')
    Label_公钥提示信息 = Label(全局变量展示框, textvariable=SV_公钥提示信息)
    Label_公钥提示信息.grid(                                                            row=3,column=2,sticky='W')
    if os.path.isfile(SV_选择公钥文件.get()):
        SV_公钥提示信息.set('公钥文件存在')
        Label_公钥提示信息['fg'] = 'green'
    else:
        SV_公钥提示信息.set('未找到')
        Label_公钥提示信息['fg'] = 'red'
    
    Button(全局变量展示框, text='选择[私钥文件]', command=DEF_按钮_选择私钥文件  ).grid(row=4,column=0,sticky='NSEW')
    Entry(全局变量展示框, textvariable=SV_选择私钥文件, width=全局变量显示宽     ).grid(row=4,column=1,sticky='NSEW')
    Label_私钥提示信息 = Label(全局变量展示框, textvariable=SV_私钥提示信息)
    Label_私钥提示信息.grid(                                                            row=4,column=2,sticky='W')
    if os.path.isfile(SV_选择私钥文件.get()):
        SV_私钥提示信息.set('私钥文件存在')
        Label_私钥提示信息['fg'] = 'green'
    else:
        SV_私钥提示信息.set('未找到')
        Label_私钥提示信息['fg'] = 'red'
    
    def DEF_按钮_修改私钥密码():
        tkinter.messagebox.showinfo(title='INFO', message='未实现')
        新窗口.attributes('-topmost', True)    ## 保持子窗口在主窗口前面
    
    Label(全局变量展示框,  text='解密[私钥密码]'                                  ).grid(row=5,column=0,sticky='NSEW')
    Entry(全局变量展示框, textvariable=SV_私钥密码, width=全局变量显示宽, show='*').grid(row=5,column=1,sticky='NSEW')
    Button(全局变量展示框, text='修改密码', command=DEF_按钮_修改私钥密码         ).grid(row=5,column=2,sticky='NSEW')
    
    Label(全局变量展示框, text='随机密码组成[数字字符]'                           ).grid(row=6,column=0,sticky='NSEW')
    Entry(全局变量展示框, textvariable=SV_随机密码数字字符集, width=全局变量显示宽).grid(row=6,column=1,sticky='NSEW')
    
    Label(全局变量展示框, text='随机密码组成[大写字母]'                           ).grid(row=7,column=0,sticky='NSEW')
    Entry(全局变量展示框, textvariable=SV_随机密码大写字母集, width=全局变量显示宽).grid(row=7,column=1,sticky='NSEW')
    
    Label(全局变量展示框, text='随机密码组成[小写字母]'                           ).grid(row=8,column=0,sticky='NSEW')
    Entry(全局变量展示框, textvariable=SV_随机密码小写字母集, width=全局变量显示宽).grid(row=8,column=1,sticky='NSEW')
    
    Label(全局变量展示框, text='随机密码组成[符号字符]'                           ).grid(row=9,column=0,sticky='NSEW')
    Entry(全局变量展示框, textvariable=SV_随机密码符号字符集, width=全局变量显示宽).grid(row=9,column=1,sticky='NSEW')
    
    ## 日志显示
    LF_日志框 = LabelFrame(全局变量展示框, text='日志(倒序)')
    LF_日志框.grid_columnconfigure(0, weight=1)
    LF_日志框.grid_rowconfigure(0, weight=1)      # (第0列, 自适应窗口高)
    TEXT_应用日志 = Text(LF_日志框, height=10, wrap='none')
    Scrollbar_日志框_竖 = Scrollbar(LF_日志框)
    Scrollbar_日志框_竖['command'] = TEXT_应用日志.yview
    Scrollbar_日志框_横 = Scrollbar(LF_日志框)
    Scrollbar_日志框_横['command'] = TEXT_应用日志.xview
    Scrollbar_日志框_横['orient'] = HORIZONTAL
    Scrollbar_日志框_竖.grid(row=0, column=1, sticky='NSEW')
    Scrollbar_日志框_横.grid(row=1, column=0, sticky='NSEW')
    TEXT_应用日志.config(xscrollcommand=Scrollbar_日志框_横.set, yscrollcommand=Scrollbar_日志框_竖.set)  # 自动设置滚动条滑动幅度
    TEXT_应用日志.grid(row=0, column=0, sticky='NSEW')
    TEXT_应用日志.tag_config('d', foreground='black')
    TEXT_应用日志.tag_config('i', foreground='green')  # 自定义文本格式 绿字
    TEXT_应用日志.tag_config('w', foreground='blue')   # 自定义文本格式 蓝字
    TEXT_应用日志.tag_config('e', backgroun='yellow', foreground='red')  # 自定义文本格式 黄底红字
    D_CACHE['LOG'] = TEXT_应用日志
    LF_日志框.grid(row=10, column=0, columnspan=3, sticky='NSEW')
    
    Label(全局变量展示框, text='[===END===]').grid(row=11, column=0, columnspan=3)

######## Sqlite3 SQL 语句函数
def DEF_SQLite3_返回_字段列表_数据列表(DB_PATH, SQL_CMD):
    数据库连接对象 = sqlite3.connect(DB_PATH)     # 尝试打开数据库文件
    try:
        游标对象 = 数据库连接对象.cursor()
    except Exception as e:
        ERROR = f'创建游标失败{e}'
        return(-1, ERROR)
    else:
        try:
            游标对象.execute(SQL_CMD)
        except Exception as e:
            ERROR = f'执行SQL语句 {SQL_CMD} 失败 {e}'
            游标对象.close()
            return(-1, ERROR)
        else:
            全部记录 = 游标对象.fetchall()                       # 获取全部查询数据记录
            游标对象_字段名列表 = 游标对象.description           # 获取查询结果的字段信息
            字段名列表 = [i[0] for i in 游标对象_字段名列表]     # 整理成字段名列表
            游标对象.close()
            return(0, (字段名列表, 全部记录))

## 执行一条SQL语句
def DEF_SQL_执行(DB_PATH, SQL_CMD):
    数据库连接对象 = sqlite3.connect(DB_PATH)     # 尝试打开数据库文件
    try:
        游标对象 = 数据库连接对象.cursor()         # 创建一个游标
    except Exception as e:
        ERROR = '创建游标失败' + str(e)
        return(1, ERROR)
    else:
        try:
            游标对象.execute(SQL_CMD)
        except Exception as e:
            游标对象.close()
            ERROR = f"执行SQL语句出错: {e}"
            return(1, ERROR)
        else:
            数据库连接对象.commit()           # 提交更改
            游标对象.close()
            数据库连接对象.close()
            return(0, '操作成功')

## 初始化数据库:PWD表不存在则新建表
def 初始化数据库():
    DB_PATH = SV_数据库文件.get()
    ## 如果数据表 PWD 不存在则新建
    SQL_CMD = '''CREATE TABLE IF NOT EXISTS PWD(
    ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    PS TEXT,
    USER TEXT,
    PASS TEXT,
    DATE timestamp not null default(datetime('now','localtime')));'''
    R = DEF_SQL_执行(DB_PATH, SQL_CMD)
    if R[0] == 0:
        INFO = "检查/初始化数据表 PWD 成功"
        LOGGING(INFO, 'i')
    else:
        ERROR = f'初始化数据表 PWD 失败:{R[1]}'
        LOGGING(ERROR, 'e')
        tkinter.messagebox.showerror(title='ERROR', message=ERROR)

## 打开/刷新数据表
def 打开数据库():
    初始化数据库()
    ## 打开数据库,读取数据库密码表内容
    DB_PATH = SV_数据库文件.get()
    SQL_CMD = 'SELECT * FROM PWD'
    R = DEF_SQLite3_返回_字段列表_数据列表(DB_PATH, SQL_CMD)
    if R[0] == 0:
        INFO = "数据库表 PWD 读取成功"
        LOGGING(INFO, 'i')
        字段列表, 数据列表 = R[1]
        字段和数据的存储和展示(字段列表, 数据列表) ## 显示在界面
    else:
        ERROR = f'数据库表 PWD 读取失败:{R[1]}'
        LOGGING(ERROR, 'e')
        tkinter.messagebox.showerror(title='ERROR', message=ERROR)


## 信息复制到剪贴板

def 解密密文(密文文本):
    ## 读取选中单元格字符串尝试还原字节类型
    try:
        还原字节码 = bytes.fromhex(密文文本)
    except Exception as E1:
        ERROR = f'还原字节码失败,格式有误(非16进制字符串形式:{E1}'
        tkinter.messagebox.showerror(title='ERROR', message=ERROR)
    else:
        ## 尝试加载私钥
        私钥文件 = SV_选择私钥文件.get()
        if 私钥文件.rstrip() == '':
            ERROR = '没有选择私钥文件'
            LOGGING(ERROR, 'e')
            tkinter.messagebox.showerror(title='ERROR', message=ERROR)
        else:
            try:
                f = open(私钥文件,'r')
            except Exception as E2:
                ERROR = f'打开私钥文件失败:{E2}'
                LOGGING(ERROR, 'e')
                tkinter.messagebox.showerror(title='ERROR', message=ERROR)
            else:
                LOGGING("打开私钥文件成功", 'i')
                STR_私钥 = f.read()
                f.close()
                
                ## 根据私钥是否被加密分别处理
                私钥密码 = SV_私钥密码.get()
                if 私钥密码 != '':
                    LOGGING("检测到私钥密码,先解密私钥,再导入使用", 'd')
                    try:
                        OJB_私钥 = RSA.importKey(STR_私钥, passphrase=私钥密码)
                    except Exception as E3:
                        ERROR = f'导入私钥失败,私钥【密码错误】或【格式错误】:{E3}'
                        LOGGING(ERROR, 'e')
                        tkinter.messagebox.showerror(title='ERROR', message=ERROR)
                    else:
                        LOGGING("导入私钥成功", 'd')
                        私钥_PKCS115 = PKCS1_v1_5.new(OJB_私钥) 
                        
                        ## 私钥尝试解密
                        随机数生成器 = Random.new().read
                        try:
                            明文字节码 = 私钥_PKCS115.decrypt(还原字节码, 随机数生成器)
                        except Exception as E4:
                            ERROR = f'私钥解密失败:{E4}'
                            LOGGING(ERROR, 'e')
                            tkinter.messagebox.showerror(title='ERROR', message=ERROR)
                        else:
                            LOGGING("私钥解密成功", 'i')
                            明文字符串 = 明文字节码.decode('utf8')
                            return(明文字符串)
                else:
                    LOGGING("未检测到私钥密码,直接导入使用", 'd')
                    try:
                        OJB_私钥 = RSA.importKey(STR_私钥)
                    except Exception as E3:
                        ERROR = f'导入私钥失败,私钥【被加密】或【格式错误】:{E3}'
                        LOGGING(ERROR, 'e')
                        tkinter.messagebox.showerror(title='ERROR', message=ERROR)
                    else:
                        LOGGING("导入私钥成功", 'd')
                        私钥_PKCS115 = PKCS1_v1_5.new(OJB_私钥) 
                        
                        ## 私钥尝试解密
                        随机数生成器 = Random.new().read
                        try:
                            明文字节码 = 私钥_PKCS115.decrypt(还原字节码, 随机数生成器)
                        except Exception as E4:
                            ERROR = f'私钥解密失败:{E4}'
                            LOGGING(ERROR, 'e')
                            tkinter.messagebox.showerror(title='ERROR', message=ERROR)
                        else:
                            LOGGING("私钥解密成功", 'i')
                            明文字符串 = 明文字节码.decode('utf8')
                            return(明文字符串)

def 复制到剪贴板_原值(选中值):
    top.clipboard_clear()           # 清空剪贴板
    top.clipboard_append(选中值)    # 写入剪贴板
    LOGGING(f"已经复制到剪贴板(原值)={选中值}", 'i')
    SV_剪贴板提示信息.set('【数据原值】 已经复制到剪贴板')

def 复制到剪贴板_解密(选中值):
    top.clipboard_clear()           # 清空剪贴板
    #top.clipboard_append(选中值)    # 写入剪贴板
    LOGGING(f"开始复制到剪贴板(解密)={选中值}", 'd')
    明文字符串 = 解密密文(选中值)
    if 明文字符串:
        top.clipboard_append(明文字符串)
        INFO = '【解密后内容】 已经复制到剪贴板'
        SV_剪贴板提示信息.set(INFO)

def 删除数据行(ID):
    LOGGING(f"DELETE ID {ID}", 'd')
    
    用户选择结果 = tkinter.messagebox.askquestion(title='删除确认', message=f'确定要删除{ID}?')
    LOGGING(f"用户选择结果={用户选择结果}", 'd')
    if 用户选择结果 == 'yes':
        DB_PATH = SV_数据库文件.get()
        SQL_CMD = f'DELETE FROM PWD WHERE ID = "{ID}"'
        LOGGING(f"SQL_CMD={SQL_CMD}", 'd')
        ## 删除记录
        R = DEF_SQL_执行(DB_PATH, SQL_CMD)
        if R[0] == 0:
            打开数据库()
            tkinter.messagebox.showinfo(title='INFO', message='删除成功')
        else:
            ERROR = f'删除记录失败:{R[1]}'
            tkinter.messagebox.showerror(title='ERROR', message=ERROR)

def 提示操作():
    tkinter.messagebox.showinfo(title='INFO', message='请使用鼠标左键操作')

def 根据编号删除整行():
    选中值 = D_CACHE['右键菜单对应数据'][0]
    删除数据行(选中值)

def 备注原值复制到剪贴板():
    选中值 = D_CACHE['右键菜单对应数据'][1]
    复制到剪贴板_原值(选中值)

def 账号原值复制到剪贴板():
    选中值 = D_CACHE['右键菜单对应数据'][2]
    复制到剪贴板_原值(选中值)

def 账号解密到剪贴板():
    选中值 = D_CACHE['右键菜单对应数据'][2]
    复制到剪贴板_解密(选中值)

def 密码原值复制到剪贴板():
    选中值 = D_CACHE['右键菜单对应数据'][3]
    复制到剪贴板_原值(选中值)

def 密码解密复制到剪贴板():
    选中值 = D_CACHE['右键菜单对应数据'][3]
    复制到剪贴板_解密(选中值)

def DEF_显示修改小窗():
    新窗口 = Toplevel()
    新窗口.title('修改')
    显示坐标 = '+500+200'
    新窗口.geometry(显示坐标)
    新窗口.grid_columnconfigure(2, weight=1)   # (第0列, 自适应窗口宽)
    新窗口.grid_columnconfigure(3, weight=1)
    新窗口.grid_columnconfigure(4, weight=1)
    新窗口.grid_rowconfigure(2, weight=1)      # (第0列, 自适应窗口高)
    
    标题框 = Frame(新窗口, bg='#FFD700')
    原值框 = Frame(新窗口, bg='#FFD700')
    新值框 = Frame(新窗口, bg='#FFD700')
    标题框.grid(row=0,column=0,sticky='W')
    原值框.grid(row=1,column=0,sticky='W')
    新值框.grid(row=2,column=0,sticky='W')
    
    if len(D_CACHE['右键菜单对应数据']) == 4:
        ID,PS,USER,PASS = D_CACHE['右键菜单对应数据']   # 兼容旧版本
    else:
        ID,PS,USER,PASS,DATE = D_CACHE['右键菜单对应数据']
    
    def 弹出右键菜单(event):
        选中控件 = event.widget
        LOGGING(f"弹出右键菜单={选中控件.grid_info()}", 'd')
        列 = 选中控件.grid_info()['column']
        if 列 == 3:      # 账号
            D_CACHE['右键菜单对应数据'] = ['','',USER_新值.get(1.0, END).rstrip('\n'),'']
        elif 列 == 4:    # 密码
            D_CACHE['右键菜单对应数据'] = ['','','',PASS_新值.get(1.0, END).rstrip('\n')]
        右键菜单_新窗口.post(event.x_root, event.y_root)   # 光标位置显示菜单
    def 转成明文():
        if D_CACHE['右键菜单对应数据'][2] != '':
            R = 解密密文(D_CACHE['右键菜单对应数据'][2])
            if R:
                USER_新值.delete(0.0, END)
                USER_新值.insert(0.0, R)
        elif D_CACHE['右键菜单对应数据'][3] != '':
            R = 解密密文(D_CACHE['右键菜单对应数据'][3])
            if R:
                PASS_新值.delete(0.0, END)
                PASS_新值.insert(0.0, R)
    def 转成密文():
        if D_CACHE['右键菜单对应数据'][2] != '':
            R = 加密明文(D_CACHE['右键菜单对应数据'][2])
            if R:
                USER_新值.delete(0.0, END)
                USER_新值.insert(0.0, R)
        elif D_CACHE['右键菜单对应数据'][3] != '':
            R = 加密明文(D_CACHE['右键菜单对应数据'][3])
            if R:
                PASS_新值.delete(0.0, END)
                PASS_新值.insert(0.0, R)
    右键菜单_新窗口 = Menu(tearoff=False)
    右键菜单_新窗口.add_command(label='转成明文', command=转成明文)
    右键菜单_新窗口.add_command(label='转成密文', command=转成密文)

    ID_标题   = Entry(新窗口, width=序号宽)
    PS_标题   = Entry(新窗口, width=备注宽)
    USER_标题 = Entry(新窗口, width=账号宽)
    PASS_标题 = Entry(新窗口, width=密码宽)
    ID_标题.insert(0, 'ID')
    PS_标题.insert(0, 'PS')
    USER_标题.insert(0, 'USER   右键菜单(明/密)转换')
    PASS_标题.insert(0, 'PASS   右键菜单(明/密)转换')
    ID_标题['stat']='readonly'
    PS_标题['stat']='readonly'
    USER_标题['stat']='readonly'
    PASS_标题['stat']='readonly'
    
    Label(标题框, text='标题').grid(row=0,column=0,sticky='W')
    ID_标题.grid(row=0,column=1,sticky='W')
    PS_标题.grid(row=0,column=2,sticky='WSNE')
    USER_标题.grid(row=0,column=3,sticky='WSNE')
    PASS_标题.grid(row=0,column=4,sticky='WSNE')
    
    ID_原值   = Entry(新窗口, width=序号宽)
    PS_原值   = Entry(新窗口, width=备注宽)
    USER_原值 = Entry(新窗口, width=账号宽)
    PASS_原值 = Entry(新窗口, width=密码宽)
    ID_原值.insert(0, ID)
    PS_原值.insert(0, PS)
    USER_原值.insert(0, USER)
    PASS_原值.insert(0, PASS)
    ID_原值['stat']='readonly'
    PS_原值['stat']='readonly'
    USER_原值['stat']='readonly'
    PASS_原值['stat']='readonly'
    Label(原值框, text='原值').grid(row=1,column=0,sticky='W')
    ID_原值.grid(row=1,column=1,sticky='W')
    PS_原值.grid(row=1,column=2,sticky='WSNE')
    USER_原值.grid(row=1,column=3,sticky='WSNE')
    PASS_原值.grid(row=1,column=4,sticky='WSNE')
    
    ID_新值   = Text(新窗口, width=序号宽, height=3)
    PS_新值   = Text(新窗口, width=备注宽, height=3)
    USER_新值 = Text(新窗口, width=账号宽, height=3)
    PASS_新值 = Text(新窗口, width=密码宽, height=3)
    USER_新值.bind("<Button-3>", 弹出右键菜单)
    PASS_新值.bind("<Button-3>", 弹出右键菜单)
    ID_新值.insert(0.0, ID)
    ID_新值.configure(state='disabled')   # 设置为不可编辑
    PS_新值.insert(0.0, PS)
    USER_新值.insert(0.0, USER)
    PASS_新值.insert(0.0, PASS)
    Label(新值框, text='新值').grid(row=2,column=0,sticky='W')
    ID_新值.grid(row=2,column=1,sticky='WSNE')
    PS_新值.grid(row=2,column=2,sticky='WSNE')
    USER_新值.grid(row=2,column=3,sticky='WSNE')
    PASS_新值.grid(row=2,column=4,sticky='WSNE')
    
    def DELETE():
        ID = ID_原值.get()
        删除数据行(ID)
        新窗口.destroy()   ## 关闭子窗口
    
    def UPDATE():
        对比结果 = ''
        L_更新内容 = []
        if PS_新值.get(1.0, END).rstrip('\n') != PS_原值.get():
            对比结果 += '备注(改)'
            VALUE = PS_新值.get(1.0, END).rstrip('\n')
            L_更新内容.append(f"PS='{VALUE}'")
        else:
            对比结果 += '备注()'
        
        if USER_新值.get(1.0, END).rstrip('\n') != USER_原值.get():
            对比结果 += '账号(改)'
            VALUE = USER_新值.get(1.0, END).rstrip('\n')
            L_更新内容.append(f"USER='{VALUE}'")
        else:
            对比结果 += '账号()'
        
        if PASS_新值.get(1.0, END).rstrip('\n') != PASS_原值.get():
            对比结果 += '密码(改)'
            VALUE = PASS_新值.get(1.0, END).rstrip('\n')
            L_更新内容.append(f"PASS='{VALUE}'")
        else:
            对比结果 += '密码()'
        
        LOGGING(对比结果, 'd')
        
        if L_更新内容 != []:
            DB_PATH = SV_数据库文件.get()
            SQL_CMD = f"UPDATE PWD SET {', '.join(L_更新内容)} WHERE ID={ID_原值.get()}"
            LOGGING(SQL_CMD, 'd')
            R = DEF_SQL_执行(DB_PATH, SQL_CMD)
            if R[0] == 0:
                LOGGING(R[1], 'i')
                新窗口.destroy()                   ## 关闭子窗口
                打开数据库() # 刷新一下
                tkinter.messagebox.showinfo(title='INFO', message=R[1])
            else:
                打开数据库() # 刷新一下
                LOGGING(R[1], 'e')
                tkinter.messagebox.showerror(title='ERROR', message=R[1])
        else:
            新窗口.attributes('-topmost', True)    ## 保持子窗口在主窗口前面
            tkinter.messagebox.showwarning(title='WARNING', message="未检测到更改")
    
    Button(新窗口, text='删除', fg='red',   command=DELETE).grid(row=3, column=0, columnspan=2,sticky='WSNE')
    Button(新窗口, text='更新', bg='#FFA500', command=UPDATE).grid(row=3, column=2, columnspan=3,sticky='WSNE')


## 创建数据框右键菜单(编号)
数据框_右键菜单_编号 = Menu(tearoff=False)
数据框_右键菜单_编号.add_command(label='修改此行(鼠标左键双击)', command=DEF_显示修改小窗)
数据框_右键菜单_编号.add_command(label='删除此行', command=根据编号删除整行)

## 创建数据框右键菜单(备注)
数据框_右键菜单_备注 = Menu(tearoff=False)
数据框_右键菜单_备注.add_command(label='原值 到 剪贴板(鼠标左键双击)', command=备注原值复制到剪贴板)

## 创建数据框右键菜单(账号)
数据框_右键菜单_账号 = Menu(tearoff=False)
数据框_右键菜单_账号.add_command(label='原值 到 剪贴板(鼠标左键双击)', command=账号原值复制到剪贴板)
数据框_右键菜单_账号.add_command(label='解密 到 剪贴板', command=账号解密到剪贴板)

## 创建数据框右键菜单(密码)
数据框_右键菜单_密码 = Menu(tearoff=False)
数据框_右键菜单_密码.add_command(label='解密 到 剪贴板(鼠标左键双击)', command=密码解密复制到剪贴板)
数据框_右键菜单_密码.add_command(label='原值 复制到 剪贴板', command=密码原值复制到剪贴板)


## 在显编框展示结果,保存结果到全局变量
def 字段和数据的存储和展示(L_字段名, LL_数据值):
    列数 = len(L_字段名)
    行数 = len(LL_数据值)
    LOGGING(f"显示字段名={L_字段名}", 'd')
    xscroll = Scrollbar(top, orient=HORIZONTAL)
    yscroll = Scrollbar(top, orient=VERTICAL)
    
    TV_数据表格 = ttk.Treeview(
        master=top,                 # 父容器
        columns=L_字段名,           # 显示的列
        show='headings',            # 隐藏首列
        xscrollcommand=xscroll.set, # x轴滚动条
        yscrollcommand=yscroll.set, # y轴滚动条
    )
    
    for 列名 in L_字段名:
        TV_数据表格.heading(column=列名, text=列名, anchor=W)   # 定义表头
        if 列名 == 'ID':
            TV_数据表格.column(column=列名, anchor=W, width=39, minwidth=39, stretch=False)  # 定义列
        elif 列名 == 'DATE':
            TV_数据表格.column(column=列名, anchor=W, width=127, minwidth=127, stretch=False)
        elif 列名 == 'PS':
            TV_数据表格.column(column=列名, anchor=W, width=290, minwidth=290)
        else:
            TV_数据表格.column(column=列名, anchor=W, width=150, minwidth=150)
    
    LL_数据值.reverse()    # 倒序显示
    for DATA in LL_数据值:
        TV_数据表格.insert('', END, values=DATA)  # 添加数据到末尾
    
    ## 嵌套函数
    def DEF_嵌套_鼠标左键双击(event):
        选中IID = TV_数据表格.identify_row(event.y)
        选中列 = TV_数据表格.identify_column(event.x)
        列号_PY_INDEX = int(选中列[1:])-1
        DEBUG = f"鼠标左键双击 选中IID={选中IID} 选中列={选中列} 列号_PY_INDEX={列号_PY_INDEX}"
        LOGGING(DEBUG, 'd')
        if 选中IID:
            L_VALUES = TV_数据表格.item(选中IID)['values']
            选中值 = L_VALUES[列号_PY_INDEX]
            if 列号_PY_INDEX == 0:    # ID列,修改用
                D_CACHE['右键菜单对应数据'] = L_VALUES
                DEF_显示修改小窗()
            elif 列号_PY_INDEX == 3:    # 密码数据
                复制到剪贴板_解密(选中值)
            else:
                复制到剪贴板_原值(选中值)
            
        else:
            LOGGING("【点出界外】应该的点在表头行了", 'w')
    
    def DEF_嵌套_鼠标右键(event):
        选中IID = TV_数据表格.identify_row(event.y)
        选中列 = TV_数据表格.identify_column(event.x)
        列号_PY_INDEX = int(选中列[1:])-1
        
        DEBUG = f"鼠标右键 选中IID={选中IID} 选中列={选中列} 列号_PY_INDEX={列号_PY_INDEX}"
        LOGGING(DEBUG, 'd')
        if 选中IID:
            TV_数据表格.selection_set(选中IID)
            L_VALUES = TV_数据表格.item(选中IID)['values']
            点击格内容 = L_VALUES[列号_PY_INDEX]
            DEBUG = f"L_VALUES={L_VALUES} 点击格内容={点击格内容}"
            LOGGING(DEBUG, 'd')
            D_CACHE['右键菜单对应数据'] = L_VALUES
            
            ## 弹出菜单
            光标X轴 = event.x_root
            光标Y轴 = event.y_root
            if 列号_PY_INDEX == 0:    # ID列,删除用
                数据框_右键菜单_编号.post(光标X轴, 光标Y轴)
            elif 列号_PY_INDEX == 2:
                数据框_右键菜单_账号.post(光标X轴, 光标Y轴)
            elif 列号_PY_INDEX == 3:    # 密码数据
                数据框_右键菜单_密码.post(光标X轴, 光标Y轴)    # 光标位置显示菜单
            else:
                数据框_右键菜单_备注.post(光标X轴, 光标Y轴)
            
        else:
            INFO = "【点出界外】应该的点在表头行了"
            LOGGING(INFO, 'i')
            D_CACHE['右键菜单对应数据'] = ''
    
    TV_数据表格.bind('<Double-Button-1>', DEF_嵌套_鼠标左键双击)   # 绑定左键双击事件
    TV_数据表格.bind('<Button-3>', DEF_嵌套_鼠标右键)              # 打开菜单

    xscroll.config(command=TV_数据表格.xview)
    xscroll.grid(row=2,column=0,sticky='NSEW')
    yscroll.config(command=TV_数据表格.yview)
    yscroll.grid(row=1,column=1,sticky='NSEW')
    TV_数据表格.grid(row=1,column=0,sticky='NSEW')  # 显示填满空间


##
def 加密明文(明文文本):
    ERROR = ''
    公钥文件 = SV_选择公钥文件.get()
    if 公钥文件.rstrip() == '':
        ERROR = '没有选择公钥文件'
    else:
        try:
            f = open(公钥文件,'r')
        except Exception as E1:
            ERROR = f'打开公钥文件失败:{E1}'
            LOGGING(ERROR, 'e')
        else:
            LOGGING("打开公钥文件成功", 'd')
            STR_公钥 = f.read()
            f.close()
            try:
                OJB_公钥 = RSA.importKey(STR_公钥)
            except Exception as E2:
                ERROR = f'导入公钥失败:{E2}'
                LOGGING(ERROR, 'e')
            else:
                公钥_PKCS115 = PKCS1_v1_5.new(OJB_公钥)
                明文字节码 = 明文文本.encode('utf8')
                try:
                    密文字节码 = 公钥_PKCS115.encrypt(明文字节码)   # 公钥加密
                except Exception as E3:
                    ERROR = f'公钥加密失败:{E3}'
                    LOGGING(ERROR, 'e')
                else:
                    LOGGING("公钥加密成功", 'i')
                    STR_HEX = Bytes2HEX_STR(密文字节码)  ## 字节类型转成16进制字符串存储到数据库
                    return(STR_HEX)
    if ERROR != '':
        ERROR += '\n请打开 "设置" 按钮处理'
        tkinter.messagebox.showerror(title='ERROR', message=ERROR)

def 存入数据库():
    备注 = Entry_备注.get().strip()
    帐号 = Entry_帐号.get().strip()
    密码 = Entry_密码.get().strip()
    if 帐号 == '' or 密码 == '':
        ERROR = '帐号/密码 中有空内容'
        LOGGING(ERROR, 'e')
        tkinter.messagebox.showerror(title='ERROR', message=ERROR)
    else:
        if IV_账号加密保存_勾选框.get() == 1:
            帐号_STR_HEX = 加密明文(帐号)
            if 帐号_STR_HEX:
                帐号 = 帐号_STR_HEX
            else:
                帐号 = ''
            
        if IV_密码加密保存_勾选框.get() == 1:
            密码_STR_HEX = 加密明文(密码)
            if 密码_STR_HEX:
                密码 = 密码_STR_HEX
            else:
                密码 = ''
        
        if 帐号 != '' and 密码 != '':
            DB_PATH = SV_数据库文件.get()
            SQL_插入数据 = f'INSERT INTO PWD (PS, USER, PASS) VALUES ("{备注}", "{帐号}", "{密码}");'
            R_INSERT = DEF_SQL_执行(DB_PATH, SQL_插入数据)
            if R_INSERT[0] == 0:
                LOGGING("保存成功", 'i')
                Entry_备注.delete(0, END)         # 界面:清空输入框
                Entry_帐号.delete(0, END)         # 界面:清空输入框
                Entry_密码.delete(0, END)         # 界面:清空输入框
                打开数据库()                      # 更新数据库显示框
            else:
                ERROR = f'插入数据失败:{R_INSERT[1]}'
                LOGGING(ERROR, 'e')
                tkinter.messagebox.showerror(title='ERROR', message=ERROR)
        else:
            ERROR = '帐号或密码加密失败'
            LOGGING(ERROR, 'e')

def 搜索备注信息():
    备注 = Entry_备注.get().strip()
    if 备注 == '':
        ERROR = '备注空内容'
        LOGGING(ERROR, 'e')
        tkinter.messagebox.showerror(title='ERROR', message=ERROR)
    else:
        DB_PATH = SV_数据库文件.get()
        SQL_CMD = f'SELECT * FROM PWD WHERE PS LIKE "%{备注}%"'
        LOGGING(f"搜索备注内容={备注} SQL_CMD={SQL_CMD}", 'd')
        R = DEF_SQLite3_返回_字段列表_数据列表(DB_PATH, SQL_CMD)
        if R[0] == 0:
            LOGGING("执行搜索语句成功", 'i')
            字段列表, 数据列表 = R[1]
            字段和数据的存储和展示(字段列表, 数据列表)
        else:
            ERROR = f'执行搜索语句失败:{R[1]}'
            LOGGING(ERROR, 'e')
            tkinter.messagebox.showerror(title='ERROR', message=ERROR)

def 生成随机密码():
    密码长度 = IV_随机密码位数.get()
    数字字符列表 = [i for i in SV_随机密码数字字符集.get()]
    大写字母列表 = [i for i in SV_随机密码大写字母集.get()]
    小写字母列表 = [i for i in SV_随机密码小写字母集.get()]
    符号字符列表 = [i for i in SV_随机密码符号字符集.get()]
    
    组合列表 = 数字字符列表 + 大写字母列表 + 小写字母列表 + 符号字符列表
    if 密码长度 > 84:
        ERROR = '过长,请设置长度 <= 84'
        tkinter.messagebox.showerror(title='ERROR', message=ERROR)
    else:
        random.shuffle(组合列表)
        随机密码 = ''
        for i in range(0, 密码长度):
            随机密码 += 组合列表[i]
        Entry_密码.delete(0, END)         # 界面:清空输入框
        Entry_密码.insert(END, 随机密码)

## 字节码转16进制格式的字符串
def Bytes2HEX_STR(Bytes):
    STR_HEX = ''
    for i in Bytes:
        STR_HEX += hex(i)[2:].zfill(2)      ## '0x50' 取 '50' 部分,如果不足2个字符,填充0
    return(STR_HEX)


操作框 = Frame(top)   #LabelFrame(top, text='操作框')

序号框 = Frame(操作框)
Label(序号框, text='序号').grid(row=0, column=0)
Entry_序号 = Entry(序号框, width=序号宽)
Entry_序号.grid(row=1, column=0, sticky='ESWN')

备注框 = Frame(操作框)
Label(备注框, text='备注').grid(row=0, column=0)
Entry_备注 = Entry(备注框, width=备注宽)
Entry_备注.grid(row=1, column=0, sticky='ESWN')
Button(备注框, text='  搜索  ', command=搜索备注信息).grid(row=2, column=0)
备注框.grid_columnconfigure(0, weight=1)

帐号框 = Frame(操作框)
Label(帐号框, text='帐号').grid(row=0, column=0)
Entry_帐号 = Entry(帐号框, width=账号宽)
Entry_帐号.grid(row=1, column=0, sticky='ESWN')
Checkbutton(帐号框, text='加密保存', variable=IV_账号加密保存_勾选框).grid(row=2,column=0)
帐号框.grid_columnconfigure(0, weight=1)

密码框 = Frame(操作框)
Label(密码框, text='密码').grid(row=0, column=0)
Entry_密码 = Entry(密码框, width=密码宽)
Entry_密码.grid(row=1, column=0, sticky='ESWN')
Checkbutton(密码框, text='加密保存', variable=IV_密码加密保存_勾选框).grid(row=2,column=0)
密码框.grid_columnconfigure(0, weight=1)

数据库操作框 = Frame(操作框) #LabelFrame(操作框, text='数据库操作框')
Button(数据库操作框, text='  设置  ', fg='red', command=DEF_显示设置小窗).grid(row=0,column=0,sticky='ESWN')
Button(数据库操作框, text='  刷新数据库  ', fg='#FFA500', command=打开数据库).grid(row=0, column=1, sticky='ESWN')
Label_剪贴板提示信息 = Label(数据库操作框, textvariable=SV_剪贴板提示信息, width=50)
Label_剪贴板提示信息['fg'] = '#FF6600'
Label_剪贴板提示信息.grid(row=0, column=2, sticky='ESWN')

保存框 = Frame(数据库操作框)
Label(保存框, text='长度'                                   ).grid(row=0, column=1, sticky='ESWN')
Entry(保存框, textvariable=IV_随机密码位数, width=4             ).grid(row=0, column=2, sticky='ESWN')
Button(保存框, text='  生成随机密码  ', command=生成随机密码).grid(row=0, column=3, sticky='ESWN')
Button(保存框, text='  保存  ', command=存入数据库, fg='red').grid(row=0, column=4, sticky='ESWN')

保存框.grid(row=0, column=3, sticky='ESWN')
数据库操作框.grid_columnconfigure(2, weight=1)

序号框.grid(row=0, column=0, sticky='NW')
备注框.grid(row=0, column=1, sticky='WSNE')
帐号框.grid(row=0, column=2, sticky='WSNE')
密码框.grid(row=0, column=3, sticky='WSNE')
数据库操作框.grid(row=1, column=0, columnspan=4, sticky='ESWN')

操作框.grid_columnconfigure(1, weight=1)   # (第2列, 自适应窗口宽)
操作框.grid_columnconfigure(2, weight=1)   # (第3列, 自适应窗口宽)
操作框.grid_columnconfigure(3, weight=1)   # (第4列, 自适应窗口宽)
操作框.grid(row=0, column=0, sticky='WSNE')

## 开软件后先执行数据库的初始化和打开
打开数据库()

# 进入消息循环
top.mainloop()

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RSA是一种非对称加密算法,用于数据的加密解密。在Python3中,可以使用`cryptography`库来实现RSA加密解密。 首先,你需要安装`cryptography`库。可以使用以下命令来安装: ``` pip install cryptography ``` 接下来,我们可以使用以下代码来生成RSA密钥对、进行加密解密操作: ```python from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import serialization # 生成RSA密钥对 private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048 ) public_key = private_key.public_key() # 将密钥保存为PEM格式 private_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() ) public_pem = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) # 加密 message = b"Hello, World!" ciphertext = public_key.encrypt( message, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) # 解密 plaintext = private_key.decrypt( ciphertext, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) # 打印结果 print("加密后的密文:", ciphertext) print("解密后的明文:", plaintext) ``` 这段代码首先生成了一个RSA私钥和对应的公钥,然后使用公钥对消息进行加密,再使用私钥对密文进行解密。最后打印出加密后的密文和解密后的明文。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值