实现对小于1GB的任何格式的文件进行AES加密
文件加密过程
文件解密过程
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
下载所需要的模块,然后双击py文件即可
计算机\HKEY_CURRENT_USER\Software\bylyl\encode
解密的时候操作与加密时候的操作类似,选择要解密的文件,输入加密时候的key,点击确认解密
注意
{
"加密文件hash":{
"primary_hash": "源文件hash",
"time": "加密时间",
"aes_key": "加密所用aes密钥",
"ensure_key": "加密时用户输入的key"
}
}
import os
import hashlib
from cryptography.fernet import Fernet
import pickle
from datetime import datetime
import functools
import time
import multiprocessing
import json
# 定义获取文件hash时读取的大小
FILE_SIZE_HASH = 4096
# 定义一次读取文件的大小
FILE_SIZE_ONCE_READ = 1024*1024*2048
# 密码本的位置
INFO_PATH = os.path.join(os.path.dirname(__file__), 'data.dat')
# 用于给密码本异或加密的密钥
INFO_KEY = 'madebylyl'
# 日记位置
LOG_PATH = os.path.join(os.path.dirname(__file__), 'log.txt')
# 支持加密文件的最大大小
FILE_MAX_SIZE = 1024*1024*1024
# 解密信息的位置
DECODE_INFO_PATH = os.path.join(os.path.dirname(__file__), 'info.dat')
def console_log(text, path=LOG_PATH):
with open(path, 'a') as f:
f.write(datetime.now().strftime("%Y-%m-%d %H-%M") + ':' + text + '\n')
# 计时器
def time_count(text):
def decorator(function):
@functools.wraps(function)
def wrapper(*args, **kwargs):
text = function(*args, **kwargs)
first = time.time()
end = time.time()
console_log("操作文件%s" % (args[0]))
return text
return wrapper
return decorator
def get_file_hash(path):
with open(path, 'rb') as f:
data = f.read(FILE_SIZE_HASH)
return hashlib.sha256(data).hexdigest()
def xor(de_data, key=INFO_KEY):
count = 0
en_data = b''
for byte in de_data:
new_byte = byte ^ ord(key[count % len(key)])
en_data += bytes([new_byte])
count += 1
return en_data
def get_data(path=INFO_PATH, key=INFO_KEY):
data = {}
if not os.path.exists(INFO_PATH):
print("We will create a file called 'data.dat' to storage infos, please remind not to delete it!")
with open(path, 'wb') as f:
f.write(xor(pickle.dumps(data)))
else:
with open(path, 'rb') as f:
data = pickle.loads(xor(f.read()))
return data
def get_decode_data(path=DECODE_INFO_PATH):
data = get_data()
with open(path, 'w') as f:
f.write(json.dumps(data, indent=2, ensure_ascii=False))
class FileDispose:
def __init__(self, path, ensure_key, data, aes_key="", file_new_name=""):
# 判断是否是本人进行的操作
self.ensure_key = ensure_key
# 判断是加密还是解密,如果是加密则生成新的密钥,否则接受传入的密钥
self.aes_key = self.get_key(aes_key)
self.aes_f = Fernet(self.aes_key)
# 根据原名称构造加密文件或解密文件的名称
self.base_path, self.file_name = os.path.split(path)
self.write_path = self.get_write_path(file_new_name)
self.file_hash = get_file_hash(path)
self.file_datas = self.read_file(path)
self.write_file(data)
def read_file(self, path):
with open(path, 'rb') as f:
while True:
data = f.read(FILE_SIZE_ONCE_READ)
if not data:
break
else:
yield data
def get_key(self, aes_key):
# 判断是加密还是解密,如果是加密则生成新的密钥,否则接受传入的密钥
if len(aes_key) == 0:
# 过程为加密
self.code = 'encode'
return Fernet.generate_key()
else:
self.code = 'decode'
return aes_key.encode()
def encode(self, de_data):
return self.aes_f.encrypt(de_data)
def decode(self, en_data):
return self.aes_f.decrypt(en_data)
def write_file(self, data):
f = open(self.write_path, 'wb')
for file_data in self.file_datas:
code = getattr(self, self.code)
f.write(code(file_data))
f.close()
self.new_file_hash = get_file_hash(self.write_path)
self.write_info(data)
# 这里应该加上信息
def write_info(self, data, info_path=INFO_PATH):
# 写入的信息应该加密
# 应该检查是否重复
if self.code == "encode":
info = {
self.new_file_hash:
{
'primary_hash': self.file_hash,
'time': datetime.now().strftime("%Y-%m-%d %H-%M"),
'aes_key': self.aes_key.decode(),
'ensure_key': self.ensure_key,
}
}
data.update(info)
elif self.code == "decode":
data.pop(self.file_hash)
with open(info_path, 'wb') as f:
f.write(xor(pickle.dumps(data)))
def get_write_path(self, file_new_name):
# 如果是加密
if self.code == 'encode':
# 默认命名
if len(file_new_name) == 0:
new_name = "加密_" + self.file_name
return os.path.join(self.base_path, new_name)
else:
return os.path.join(self.base_path, file_new_name)
# 如果是解密
if self.code == 'decode':
if len(file_new_name) == 0:
new_name = "解密_" + self.file_name
return os.path.join(self.base_path, new_name)
else:
return os.path.join(self.base_path, file_new_name)
@time_count("加密")
def encode_file(path, ensure_key):
data = get_data()
file_hash = get_file_hash(path)
if file_hash in data:
text = "文件已经被加密!"
elif file_hash in str(data):
text = "该文件已经存在加密版本"
else:
text = "ok"
t = multiprocessing.Process(target=FileDispose, args=(path, ensure_key, data))
t.start()
return text
@time_count("解密")
def decode_file(path, ensure_key):
data = get_data()
file_hash = get_file_hash(path)
if not file_hash in data:
text = "该文件没有被加密"
else:
file_info = data.get(file_hash)
if ensure_key != file_info.get("ensure_key"):
text = "密钥不正确"
else:
text = "ok"
t = multiprocessing.Process(target=FileDispose, args=(path, file_info.get("ensure_key"), data, file_info.get("aes_key")))
t.start()
return text
# #######################UI界面############################
import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QComboBox, QLabel, QApplication, QFileDialog, QMessageBox, QLineEdit, QInputDialog
from PyQt5.QtCore import QSettings
class MyUI(QWidget):
def __init__(self):
super().__init__()
# 从注册表获取初始密码
self.get_primary_key()
self.initUI()
def get_primary_key(self):
settings = QSettings("bylyl", "encode")
value = settings.value("primary_key", '/')
if value == "/":
# 初始化密码
self.primary_key = self.init_key()
else:
self.primary_key = value
def init_key(self):
while True:
while True:
pwd1, ok = QInputDialog.getText(self, "初始密码", "!!!请输入初始密码")
if not ok:
sys.exit()
else:
if pwd1 == '' or pwd1 == '/':
self.show_error("密码不合法!")
else:
break
while True:
pwd2, ok = QInputDialog.getText(self, "初始密码", "!!!请确认初始密码")
if ok:
if pwd2 != pwd1:
self.show_error("两次输入密码不一致")
else:
# 写入注册表
settings = QSettings("bylyl", "encode")
settings.setValue("primary_key", pwd1)
self.show_info("请牢记密码》{0}".format(pwd1))
return pwd1
def initUI(self):
self.btn1 = QPushButton('选择文件', self)
self.btn1.move(20, 20)
self.btn1.clicked.connect(self.choose_file)
self.le = QLineEdit(self, )
self.le.move(130, 20)
self.le.resize(350, 30)
self.le.setPlaceholderText("输入文件完整路径或在左边选择文件")
# 输入密码框
self.le1 = QLineEdit(self, )
self.le1.move(130, 70)
self.le1.resize(200, 30)
self.le1.setPlaceholderText("请输入密码")
self.le1.setEchoMode(self.le1.Password)
self.le2 = QLineEdit(self, )
self.le2.move(130, 110)
self.le2.resize(200, 30)
self.le2.setPlaceholderText("确认密码")
self.le2.setEchoMode(self.le1.Password)
# 设置标签
self.info = QLabel(self)
self.info.move(60, 160)
self.info.setText("模式")
# 设置下拉选项框
self.box = QComboBox(self)
self.box.addItems(("encode", "decode")),
self.box.move(130, 155)
self.box.currentIndexChanged.connect(self.change_mode)
# 确认按钮
self.btn2 = QPushButton("确认加密",self)
self.btn2.resize(150,70)
self.btn2.move(350, 80)
self.btn2.clicked.connect(self.check_file)
# 忘记密码的按钮
self.btn3 = QPushButton("导出密码", self)
self.btn3.resize(110,50)
self.btn3.move(10, 74)
self.btn3.clicked.connect(self.get_secret)
self.setGeometry(500, 500, 600, 200)
self.setWindowTitle("加密软件:made_by_lyl")
self.show()
def choose_file(self):
fname = QFileDialog.getOpenFileName(self, "选择文件", '/home')
if fname[0]:
if os.path.getsize(fname[0]) > FILE_MAX_SIZE:
self.show_error("当前只支持小于1GB文件")
else:
self.le.setText(str(fname[0]))
def change_mode(self):
text = self.box.currentText()
if text == "encode":
self.btn2.setText("确认加密")
elif text == "decode":
self.btn2.setText("确认解密")
def check_file(self):
mode = self.box.currentText()
path = self.le.text()
password1 = self.le1.text().strip()
password2 = self.le2.text().strip()
if len(password1) == 0:
self.show_error("密码不能为空")
elif password1 != password2:
self.show_error("两次输入密码不一致")
elif len(path) == 0:
self.show_error("请输入路径")
elif not os.path.isfile(path):
self.show_error("路径不合法")
elif os.path.getsize(path) > FILE_MAX_SIZE:
self.show_error("当前只支持小于1GB文件")
self.le.clear()
else:
if mode == "encode":
text = encode_file(path, password1)
if text.lower() != "ok":
self.show_error(text)
else:
self.show_info("已进入后台加密")
self.show_im("请牢记密码:{0}".format(password1))
elif mode == "decode":
text = decode_file(path, password1)
if text.lower() != "ok":
self.show_error(text)
else:
self.show_info("已进入后台解密")
def get_secret(self):
text, ok = QInputDialog.getText(self, "导出密码", "请输入初始密码", QLineEdit.Password)
if ok:
if text == self.primary_key:
get_decode_data()
self.show_info("请查看文件info.dat")
else:
self.show_error("密码错误")
def show_info(self, text):
QMessageBox.information(self, "提示", text)
def show_im(self, text):
QMessageBox.critical(self, "重要", text)
def show_error(self, text):
QMessageBox.warning(self, "错误", text)
def main():
# 打包时不加这一条pyinstaller会默认不支持多进程
multiprocessing.freeze_support()
try:
app = QApplication(sys.argv)
ex = MyUI()
sys.exit(app.exec_())
except Exception as e:
console_log(e)
if __name__ == '__main__':
main()