用python实现AES算法批量加密文件

之前用异或算法自己加密文件,感觉密码还是太弱,在网上传输需要优秀的算法。于是用AES对称加密算法实现对电脑文件的加密。

实现主要有两个.py文件,一个是ecy.py,主要写了加密的核心算法。另一个是QT Desinger生成的界面文件,用信号与槽与核心算法链接。

一、算法文件

主要实现一个文件夹中文件的遍历,对每一个文件进行加解密操作,生成的新文件还放在该文件夹中。老文件可以删除,也可以保留。还可以选择是否对文件重新命名。下面是ecy.py文件。

from Crypto.Cipher  import AES
import os

# 密钥(key), 密斯偏移量(iv) CBC模式加密

BLOCK_SIZE = 16  # Bytes
def pad(bs):
    rbs=(BLOCK_SIZE - len(bs) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(bs) % BLOCK_SIZE)
    rb=rbs.encode('utf8')
    rb=bs+rb
    return rb

unpad = lambda s: s[:-ord(s[len(s) - 1:])]
vi = b'0102030405060708'

def encrypt_all(path1,key,flag,remove,rename):
    """
    :param path1: 操作文件的路径
    :param key: 密码
    :param flag: 加解密标志。False位加密,True为解密
    :param remove: False是不删除原文件,True删除源文件。
    :param rename: False是添加前缀,True是不添加前缀。
    :return:
    """
    for root, dirs, files in os.walk(path1):
        for name in files:
            path3 = os.path.join(root, name)
            if not flag:
                name1 = "ej_" + name
            else:
                name1 = "dj_" + name
            path4 = os.path.join(root, name1)
            print(str(path3))
            fin = open(path3, "rb+")
            fou = open(path4, "ab+")
            if not flag:
                while True:
                    byt1 = fin.read(1023)
                    if not byt1:
                        break
                    byt2 = AES_Encrypt(key, byt1)
                    fou.write(byt2)
            else:
                while True:
                    byt1 = fin.read(1024)
                    if not byt1:
                        break
                    byt2 = AES_Decrypt(key, byt1)
                    fou.write(byt2)
            fin.close()
            fou.close()
            if remove:
                os.remove(path3)
                if rename:
                    os.rename(path4, path3)
def make_key(key):
    '''
        密码必须是16字节的。如果小于16字节,如果小于16位,直接复制。如果大于16位,直接剪掉。
        等下要考虑密码为空的情况。
    '''
    newKey = key.encode('utf8')
    l = len(newKey)
    while l<16:
        newKey=newKey+newKey
        l = len(newKey)
    newKey=newKey[:16]
    return newKey
def AES_Encrypt(key, data):
    """
    :param key: 密码,二进制数据
    :param data: 需加密的数据,二进制数据
    :return: 已加密的二进制数据
    """
    data = pad(data)
    # 字符串补位
    cipher = AES.new(key, AES.MODE_CBC, vi)
    encryptedbytes = cipher.encrypt(data)
    return encryptedbytes
def AES_Decrypt(key, data):
    """
    :param key: 密码
    :param data: 待解密的数据。这里必须使用经过加密的文件,好像加过密的文件,他有特殊的标识,
    能够认出来。不是经这个程序加密的数据,会报错。
    :return:解密后的数据。
    """
    cipher = AES.new(key, AES.MODE_CBC, vi)
    text_decrypted = cipher.decrypt(data)
    # 去补位
    text_decrypted = unpad(text_decrypted)
    return text_decrypted

主要的两个函数是加密的AES_Encrypt(key, data)和解密的AES_Decrypt(key, data)两个函数。使用CBC模式对数据进行加解密。详细原理可参考Python 实现 AES 加密/解密.

其中AES_Encrypt(key, data)输入的字节数可以是任意的,他输出的加密数据不是任务的,而是比data字节数大,且最小的16倍的字节数。这里data取1023个字节,则输出的加密块就是1024个字节。

AES_Decrypt(key, data)解密函数必须输入的是经加密函数加密的数据,不然会报错。

二、界面文件

下面是主程序入口,也是界面文件main.py

import sys
import ecy
from PyQt5 import QtCore, QtGui, QtWidgets


flag_Pr=False#False是添加前缀,True是不添加前缀。
flag_Rm=False#False是不删除原文件,True删除源文件。
flag_Ec=False#False是加密,True是解密

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(100, 10, 581, 78))
        self.label_3.setObjectName("label_3")
        self.checkBox_2 = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox_2.setGeometry(QtCore.QRect(440, 260, 171, 19))
        self.checkBox_2.setObjectName("checkBox_2")
        self.checkBox = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox.setGeometry(QtCore.QRect(240, 260, 111, 19))
        self.checkBox.setObjectName("checkBox")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(80, 130, 120, 26))
        self.label.setObjectName("label")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(90, 200, 98, 26))
        self.label_4.setObjectName("label_4")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(100, 260, 72, 26))
        self.label_2.setObjectName("label_2")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(210, 130, 451, 24))
        self.lineEdit.setObjectName("lineEdit")
        self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_2.setGeometry(QtCore.QRect(210, 200, 451, 24))
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(250, 400, 93, 28))
        self.pushButton.setObjectName("pushButton")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(420, 400, 93, 28))
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 27))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.checkBox.clicked['bool'].connect(self.removeOldFile)
        self.checkBox_2.clicked['bool'].connect(self.hasProfix)
        self.pushButton.clicked['bool'].connect(self.enCry)
        self.pushButton_2.clicked['bool'].connect(self.deCry)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def removeOldFile(self):
        global flag_Rm,flag_Pr
        flag_Rm=not flag_Rm
        if flag_Rm:
            #删除源文件的情况
            self.checkBox_2.setEnabled(True)
        else:
            self.checkBox_2.setEnabled(False)
            self.checkBox_2.setCheckState(2)
            flag_Pr=False
    def hasProfix(self):
        global flag_Pr,flag_Rm
        flag_Pr = not flag_Pr
    def enCry(self):
        global flag_Pr, flag_Rm
        lts=self.lineEdit.text()
        if lts == "" or lts == "Please enter a Path!":
            self.lineEdit.setText("Please enter a Path!")
        else:
            strs = lts.split("\\")
            str = "\\\\".join(strs)
            lts2 = self.lineEdit_2.text()
            if lts2 == "" or lts2 == "Please enter a Key!":
                self.lineEdit_2.setText("Please enter a Key!")
            else:
                newkey = ecy.make_key(lts2)
                ecy.encrypt_all(str,newkey,False,flag_Rm,flag_Pr)
    def deCry(self):
        global flag_Pr, flag_Rm
        lts = self.lineEdit.text()
        if lts == "" or lts == "Please enter a Path!":
            self.lineEdit.setText("Please enter a Path!")
        else:
            strs = lts.split("\\")
            str = "\\\\".join(strs)
            lts2 = self.lineEdit_2.text()
            if lts2 == "" or lts2 == "Please enter a Key!":
                self.lineEdit_2.setText("Please enter a Key!")
            else:
                newkey = ecy.make_key(lts2)
                ecy.encrypt_all(str, newkey, True, flag_Rm, flag_Pr)
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label_3.setText(_translate("MainWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:18pt; font-weight:600;\">文件加解密程序</span></p></body></html>"))
        self.checkBox_2.setText(_translate("MainWindow", "生成文件有前缀"))
        self.checkBox_2.setEnabled(False)
        self.checkBox_2.setCheckState(2)
        self.checkBox.setText(_translate("MainWindow", "删除原文件"))
        self.label.setText(_translate("MainWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:14pt; font-weight:600;\">文件路径:</span></p></body></html>"))
        self.label_4.setText(_translate("MainWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:14pt; font-weight:600;\">密码:</span></p></body></html>"))
        self.label_2.setText(_translate("MainWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:14pt; font-weight:600;\">选项:</span></p></body></html>"))
        self.pushButton.setText(_translate("MainWindow", "加密"))
        self.pushButton_2.setText(_translate("MainWindow", "解密"))


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)  # 创建一个QApplication,即将开发的软件app
    MainWindow = QtWidgets.QMainWindow()  # QMainWindow装载需要的组件
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)  # 执行类中的setupUi方法
    MainWindow.show()
    sys.exit(app.exec_())  # exit()或点击按钮退出app

该文件由QT Desinger自动生成,如何生成可参考python可视化开发,主要改写了4个槽函数,具体可参考代码。

三、意料之外的错误

在用pyinstaller进行发布的时候,出现了如下错误。

 经过很长时间的尝试,感觉这个错误出引用的两个模块有冲突。单独有from PyQt5 import QtCore, QtGui, QtWidgets这个模块在的时候,运行没有问题。可以运行出UI解密。当引入from Crypto.Cipher import AES这个模块的时候,在python环境中运行也没有问题,但用pyinstaller发布出来会有冲突,没法运行,一运行就会报上面的错误。

猜你喜欢

转载自blog.csdn.net/qq_22329695/article/details/125767386
今日推荐