简单GUI实现python SM4算法加解密文件(1.0)

一、整体架构

整体程序由四部分组成,分别为mygui.py实现图形化界面,myencode.py实现调用SM4算法加密,mydecode.py实现调用SM4算法解密,SM4.py实现SM4整体算法。

二、分部思路及实现代码

(一) GUI部分

整个GUI实现输入密钥、加密密钥、回显被加密密钥、加密文件、解密文件几个功能。其中加密文件使用的密钥为先通过用户端输入自定义的密钥,点击加密密钥后会使用SM4算法加密输入的密钥,对密钥进行派生,更加安全。派生后的密钥为加密文件真正使用的密钥。加解密用户端都可以随时输入密钥。
同时可以自定义加密填充文件的名称。mygui.py部分代码如下
import tkinter as tk
from tkinter import filedialog
import os
import mydecode
import myencode

class FileEncryptionApp:
    def __init__(self, root):
        # 常量定义
        self.key = b''
        self.encoded_key = b''
        # 设置整个弹窗属性
        self.root = root
        self.root.title("SM4算法文件加解密")
        screen_width = root.winfo_screenwidth()
        screen_height = root.winfo_screenheight()
        window_width = 800
        window_height = 600
        x = (screen_width - window_width) // 2
        y = (screen_height - window_height) // 2
        root.geometry(f"{window_width}x{window_height}+{x}+{y}")

        # 设置输入密钥文本框属性
        self.input_frame = tk.Text(root)
        self.input_frame.pack(side=tk.LEFT, padx=0, pady=0, fill=tk.BOTH, expand=True)
        self.text_box = tk.Text(self.input_frame, height=4, width=10)
        self.text_box.configure(bg="white")
        self.text_box.configure(font=("Times New Roman", 14))
        self.text_box.pack(fill=tk.BOTH, expand=True)

        # 设置密钥加密结果显示文本框
        self.output_frame = tk.Frame(root)
        self.output_frame.pack(side=tk.LEFT, padx=0, pady=0, fill=tk.BOTH, expand=True)
        self.encrypted_text_box = tk.Text(self.output_frame, height=4, width=10, state=tk.DISABLED)
        self.encrypted_text_box.configure(bg="white")
        self.encrypted_text_box.configure(font=("Times New Roman", 14))
        self.encrypted_text_box.pack(fill=tk.BOTH, expand=True)

        # 设置上传文件、加密文件、解密文件按钮及对应方法名字
        self.button_frame = tk.Frame(root)
        self.button_frame.pack(side=tk.LEFT, padx=10, pady=10)
        self.upload_button = tk.Button(root, text="加密密钥", command=self.encrypt_key)
        self.upload_button.pack()
        self.upload_button = tk.Button(root, text="上传文件", command=self.upload_file)
        self.upload_button.pack()
        self.encrypt_button = tk.Button(root, text="加密文件", command=self.encrypt_file)
        self.encrypt_button.pack()
        self.decrypt_button = tk.Button(root, text="解密文件", command=self.decrypt_file)
        self.decrypt_button.pack()
        self.file_label = tk.Label(root, text="")
        self.file_label.pack()

    # 获取加密密钥
    def encrypt_key(self):
        self.key = self.text_box.get(1.0, tk.END)
        # 去掉末尾换行符
        self.key = self.key[:-1]
        print(self.key.encode())
        self.encoded_key = myencode.encode_key(self.key.encode(), self.key.encode())
        self.encrypted_text_box.configure(state=tk.NORMAL)  # 设置为可编辑状态
        self.encrypted_text_box.delete(1.0, tk.END)  # 清空文本框内容
        self.encrypted_text_box.insert(tk.END, self.encoded_key)  # 插入加密密钥
        self.encrypted_text_box.configure(state=tk.DISABLED)  # 设置为不可编辑状态
        # print(self.encoded_key)

    def upload_file(self):
        self.file_path = filedialog.askopenfilename()
        self.file_label.config(text=f"已上传文件:{os.path.basename(self.file_path)}")
        print(self.file_path)

    def encrypt_file(self):
        if hasattr(self, 'file_path'):
            self.num = len(self.file_path)
            # print(self.file_path[num - 4:])
            myencode.encode(self.encoded_key, self.file_path, "padding" + self.file_path[self.num - 4:], "encode" + self.file_path[self.num - 4:])
            tk.messagebox.showinfo("加密成功", "文件加密成功!")
        else:
            tk.messagebox.showerror("错误", "请先上传文件!")

    def decrypt_file(self):
        if hasattr(self, 'file_path'):
            # num = len(self.file_path)
            # print(self.file_path[num - 4:])
            mydecode.decode(self.encoded_key, "encode" + self.file_path[self.num - 4:], "decode" + self.file_path[self.num - 4:])
            tk.messagebox.showinfo("解密成功", "文件解密成功!")
        else:
            tk.messagebox.showerror("错误", "请先上传文件!")

if __name__ == "__main__":
    root = tk.Tk()
    app = FileEncryptionApp(root)
    root.mainloop()

(二) SM4算法部分

整个算法中分别定义了线性变换函数、非线性变换函数、密钥扩展函数、加解密函数几个部分,其中加解密时使用的为同一函数,加密用时较短,其中ROUND参数可以自行改变,同时算法代码允许输入msg的长度超过32字节。填充输入msg(如果msg长度不是32倍数)函数在myencode.py中实现。SM4算法部分代码如下:
ROUND = 32

FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]
CK = [0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
      0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
      0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
      0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
      0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
      0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
      0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
      0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279]
SBOX = [[0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05],
        [0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99],
        [0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62],
        [0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6],
        [0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8],
        [0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35],
        [0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87],
        [0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e],
        [0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1],
        [0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3],
        [0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f],
        [0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51],
        [0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8],
        [0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0],
        [0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84],
        [0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48]]
# 线性变换函数
def line_mes(x):
    y = x ^ (((x << 2) | (x >> 30)) & 0xffffffff) ^ (((x << 10) | (x >> 22)) & 0xffffffff) ^ (((x << 18) | (x >> 14)) & 0xffffffff) ^ (((x << 24) | (x >> 8)) & 0xffffffff)
    return y
def line_key(x):
    y = x ^ (((x << 13) | (x >> 19)) & 0xffffffff) ^ (((x << 23) | (x >> 9)) & 0xffffffff)
    return y
# 非线性变换函数
def ST(a, b, c, d, n):
    e =  '{:08x}'.format(a ^ b ^ c ^ d)
    a0 = SBOX[int(e[0], 16)][int(e[1], 16)]
    b0 = SBOX[int(e[2], 16)][int(e[3], 16)]
    c0 = SBOX[int(e[4], 16)][int(e[5], 16)]
    d0 = SBOX[int(e[6], 16)][int(e[7], 16)]
    X = int('{:02x}'.format(a0) + '{:02x}'.format(b0) + '{:02x}'.format(c0) + '{:02x}'.format(d0), 16)
    if n == 0:
        Y = line_mes(X) #加密消息使用的线性变换函数
    else:
        Y = line_key(X)  #加密密钥使用的线性变换函数
    return Y
# 密钥扩展函数
def key_expansion(mk):
    rk = []
    mk0 = int(mk[:8], 16)
    mk1 = int(mk[8:16], 16)
    mk2 = int(mk[16:24], 16)
    mk3 = int(mk[24:32], 16)
    k0 = mk0 ^ FK[0]
    k1 = mk1 ^ FK[1]
    k2 = mk2 ^ FK[2]
    k3 = mk3 ^ FK[3]
    for i in range(ROUND):
        rk.append(k0 ^ ST(k1, k2, k3, CK[i], 1))
        k0 = k1
        k1 = k2
        k2 = k3
        k3 = rk[i]
    return rk
# 加密解密函数
def cryption(msg, mk, mode):
    rk = key_expansion(mk)
    if mode == 1:
        rk = rk[::-1]
    x0 = int(msg[:8], 16)
    x1 = int(msg[8:16], 16)
    x2 = int(msg[16:24], 16)
    x3 = int(msg[24:32], 16)
    for i in range(ROUND):
        temp = x0 ^ ST(x1, x2, x3, rk[i], 0)
        x0 = x1
        x1 = x2
        x2 = x3
        x3 = temp #反序运算
    cipher = '{:08x}'.format(x3) + '{:08x}'.format(x2) + '{:08x}'.format(x1) + '{:08x}'.format(x0)
    return cipher
msg = b'0123456789abcdeffedcba9876543210'
mk = b'0123456789abcdeffedcba9876543210'
# len_num = 7
def encode():
    if len(msg) % 32 == 0:
        cipher = cryption(msg.decode(), mk.decode(), 0)
        return cipher
    else:
        return "Encode error"
def decode():
    if len(msg) % 32 == 0:
        plain = cryption(msg, mk.decode(), 1)
        return plain
    else:
        return "Decode error"

(三) myencode部分

在myencode部分对输入数据进行了填充处理,保证数据长度为32倍数。同时如果加密成功完成,会在控制端输出ENCODE!字符。myencode部分代码如下:
import SM4
import binascii

# 口令派生加密密钥
# parse = b'0123456789abcdeffedcba9876543210'
# key = b'0123456789abcdeffedcba9876543210'
# SM4.msg = parse
# SM4.mk = key
# SM4.len_num = int(len(parse) / 32)
# file_key = SM4.encode()
def encode_key(parse, key):
    SM4.msg = parse
    SM4.mk = key
    SM4.len_num = int(len(parse) / 32)
    file_key = SM4.encode()
    return file_key
# file_key = b'681edf34d206965e86b3e94f536e4246'
# 常量定义
def encode(key, old_file_name, padding_file_name, encode_file_name):
    # parse = b'0123456789abcdeffedcba9876543210'
    # key = b'0123456789abcdeffedcba9876543210'
    file_key = key
    data = ''
    new_data = ''
    old_file_name = old_file_name
    padding_file_name = padding_file_name
    encode_file_name = encode_file_name
    with open(old_file_name, "rb") as fin:
        with open(padding_file_name, "w") as fout:
            in_data = fin.read()
            print(in_data.hex())
            fout.write(in_data.hex())
            for i in range(32 - len(in_data) % 32):
                fout.write('00')
        with open(padding_file_name, "rb") as fin1:
            data = fin1.read()
            print(data)
    for i in range(int(len(data) / 32)):
        SM4.msg = data[i * 32:(i + 1) * 32]
        SM4.mk = file_key.encode()
        new_data += SM4.encode()
    with open(encode_file_name, "wb") as fout1:
        for i in new_data:
            fout1.write(i.encode())
    print("ENCODE!")

(四) mydecode部分

在mydecode部分实现了自定义解密文件名称,调用函数逻辑和顺序与加密部分大体相同。解密成功会在终端回显DECODE!字符。mydecode部分代码如下:
import SM4

# 口令派生加密密钥
# def encode_key(parse, key):
#     SM4.msg = parse
#     SM4.mk = key
#     SM4.len_num = int(len(parse) / 32)
#     file_key = SM4.encode()
#     return file_key

# file_key = b'681edf34d206965e86b3e94f536e4246'
def decode(key, encode_file_name, decode_file_name):
    # parse = b'0123456789abcdeffedcba9876543210'
    # key = b'0123456789abcdeffedcba9876543210'
    # file_key = encode_key(parse, key)
    # 常量定义
    file_key = key
    data = b''
    new_data = b''
    encode_file_name = encode_file_name
    decode_file_name = decode_file_name
    with open(encode_file_name, "rb") as fin:
        data = fin.read()
    for i in range(int(len(data) / 32)):
        SM4.msg = data[i * 32:(i + 1) * 32]
        SM4.mk = file_key.encode()
        a = SM4.decode()
        new_data += a.encode()
    with open(decode_file_name, "wb") as fout1:
        # 将字节转化为字符串再作为十六进制转化为字符串
        data1 = new_data.decode()
        data1 = bytes.fromhex(data1)
        fout1.write(data1)
    print("DECODE!")

三、实现界面演示

(一) 加密密钥界面

image

(二) 上传文件界面

image

(三) 加密文件界面

image

(四) 解密文件界面及源文件和解密后文件对比(text.txt和decode.txt)

image

image

<think>好的,用户问“padx是什么”,我需要用中文回答。首先,回忆之前的对话,用户是在处理Tkinter布局的问题,特别是想让Combo控件和下方按钮对齐宽度。之前的建议里提到了使用grid布局,设置columnspan和权重,还提到了padx参数。现在用户可能对padx的具体作用不太清楚,需要详细解释。 首先,padx是Tkinter中用于控制控件水平方向外边距的一个选项。常见于grid和pack布局管理器。需要解释它的基本定义,比如是“padding in X direction”的缩写,控制左右两侧的外部间距。然后,区分padx和pady,一个控制水平,一个控制垂直。 接下来,可能需要举例说明如何使用padx。比如在grid布局中,给按钮设置padx=5,这样按钮左右会有5像素的空白,避免紧贴其他控件或窗口边缘。同时,可以提到padx可以接受两个值的元组,分别代表左边和右边的间距,比如padx=(10,5),左边10,右边5。 还需要强调padx和内部填充(ipadx)的区别,padx是外部间距,ipadx是控件内部的扩展,比如让按钮变宽。这时候可能需要用例子对比,比如Button(..., padx=5)和Button(..., ipadx=5)的不同效果。 然后,结合用户之前的场景,他们在使用grid布局对齐Combo和按钮,正确使用padx可以调整控件之间的间距,让布局更美观。比如Combo和按钮都设置了padx=5,这样它们的左右边距一致,对齐更准确。 还要考虑用户可能的常见错误,比如误用ipadx导致控件尺寸变化,或者没有统一padx值导致布局不整齐。这时候可以提醒用户检查代码中的padx设置,确保一致。 最后,总结padx的作用,强调它在布局中的重要性,帮助用户更好地控制界面元素的位置和间距。可能需要用代码示例来直观展示不同padx值的效果,比如没有padx、padx=5、padx=(10,5)的情况,让用户更容易理解。</think>在 **Tkinter/Python GUI 编程**中,`padx` 是用于控制控件 **水平方向外部间距** 的关键参数。以下是详细解释: --- ### **1. 基本定义** - **作用**:`padx`(`padding in X direction`)定义控件 **左右两侧** 与相邻控件或容器边缘的**空白区域**。 - **适用范围**:`grid()` 或 `pack()` 布局中的控件(如按钮、输入框、标签等)。 --- ### **2. 使用场景** #### **示例代码** ```python button = tk.Button(root, text="按钮") button.grid(row=0, column=0, padx=10) # 按钮左右各保留10像素空白 ``` #### **效果对比** - **无 `padx`**:控件紧贴容器边缘或相邻控件。 ![无padx](https://via.placeholder.com/150x50/FFFFFF/000000?text=No+padx) - **有 `padx=10`**:控件左右出现间距。 ![有padx](https://via.placeholder.com/150x50/FFFFFF/000000?text=padx%3D10) --- ### **3. 参数形式** - **单值**:`padx=5` → 左右两侧均保留5像素空白。 - **双值元组**:`padx=(10,5)` → 左间距10像素,右间距5像素(精细化控制不对称间距)。 --- ### **4. 与相关参数对比** | 参数 | 作用方向 | 控制对象 | 示例 | |--------|----------------|----------------|---------------------| | `padx` | 水平方向 | **外部**间距 | `button.grid(padx=5)` | | `pady` | 垂直方向 | **外部**间距 | `button.grid(pady=5)` | | `ipadx`| 水平方向 | **内部**扩展 | `button.grid(ipadx=5)` | --- ### **5. 在你的场景中的作用** 在你之前的代码中: ```python self.plipTool_Combo.grid(..., padx=5, ...) button.grid(..., padx=5, ...) ``` - **统一性**:通过设置相同的 `padx` 值,确保 Combo 控件和按钮的**左右边距一致**,避免因间距差异导致视觉错位。 - **美观性**:防止控件紧贴容器边缘,提升界面可读性。 --- ### **常见错误** - **混淆 `padx` 和 `ipadx`**:`ipadx` 会直接改变控件宽度(如按钮变宽),而 `padx` 仅调整外部间距。 - **忽略双值元组**:若需要不对称间距(例如左侧10px、右侧5px),需显式指定 `padx=(10,5)`。 通过合理使用 `padx`,可精准控制控件在水平方向上的布局对齐。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值