2022秋季信息安全实验3
一、题目:了解CryptoAPI编程(crypto_api_doc.pdf),尝试用CryptoAPI或OpenSSL或其它加解密编程接口写一个简单的加解密程序(完成本地文件的加密解密,参考SFT的本地文件加解密功能,要有界面),开发工具不限。
二、效果图
三、加解密步骤
1.进行本地文件加密
(1)读取文件1.txt文件(此文件自己新建,内容自定义)
(2)输入Key,即Password:123456789,点击AES Encrypt(AES加密),出现Encryption Finished即加密成功。
(3)加密后的文件内容
2.进行本地文件解密
(1)读取加密好的1.txt.fcsenc文件
(2)点击AES Decrypt(AES 解密),出现Decryption Finished即解密成功
(3)解密后的文件内容与最开始的一致
四、代码实现
1.项目架构
2.实验三.py
FCS_Version = 'V1.8' # DON'T REMOVE OR MOVE THIS LINE
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
from tkinter import scrolledtext
from PIL import Image, ImageTk
import os
from os import path
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from Crypto.Cipher import AES
import logging
import pyperclip
import hashlib
import shutil
###--- Methods ---###
def UpdateCheck():
import requests
Logger('info',"Checking for new version...")
try:
try:
last_version = int ( requests.get("https://raw.githubusercontent.com/JimChr-R4GN4R/FilesCrypterSavior/main/FilesCrypterSavior.py").text.split('\n')[0].split(' ')[2].replace("'","").replace('V','').replace('.','').replace('\r','') )
cur_version = int( FCS_Version[1:].replace('.','') )
if last_version > cur_version:
Logger('info',"There is a newer version! Please update FCS.")
else:
Logger('info',"You are up to date.")
except ValueError:
Logger('error',"Please check back later or contact with R4GN4R. Probably there is an update on the way!")
except requests.exceptions.RequestException:
Logger('error',"[UC-1] Please check your internet connection.")
except requests.exceptions.HTTPError:
Logger('error',"[UC-2] Http Error.")
except requests.exceptions.ConnectionError:
Logger('error',"[UC-3] Error Connecting.")
except requests.exceptions.Timeout:
Logger('error',"[UC-4] Timeout Error.")
def Logger(mtype, message):
Logging_window.configure(state='normal') # Enable Logging window to add messages
if mtype == 'warn':
message = '[Warning] - ' + message
elif mtype =='info':
message = '[Info] - ' + message
elif mtype =='imp':
message = '[Important] - ' + message
elif mtype =='error':
message = '[Error] - ' + message
message += '\n'
Logging_window.insert(INSERT, message)
Logging_window.configure(state='disabled')# Disable Logging window to not edit messages
def KeyRandomGenerator():
key_input_encrypt.delete(0,END)
key = get_random_bytes(ChangeKeyGenBits.keybytes).hex()
key_input_label_encrypt['text'] = "Key (Hex):"
key_input_encrypt.insert(0,key)
pyperclip.copy(key) # Copy hex key to clickboard
Logger('info',"Key has been copied in your clickboard.")
def ChangeKeyGenBits(event):
if '128bits' in AES_encrypt_random_key_generator_button['text']:
AES_encrypt_random_key_generator_button['text'] = 'Generate 192bits Key (Hex)'
ChangeKeyGenBits.keybytes = 192//8
elif '192bits' in AES_encrypt_random_key_generator_button['text']:
AES_encrypt_random_key_generator_button['text'] = 'Generate 256bits Key (Hex)'
ChangeKeyGenBits.keybytes = 256//8
else:
AES_encrypt_random_key_generator_button['text'] = 'Generate 128bits Key (Hex)'
ChangeKeyGenBits.keybytes = 128//8
def EncryptKeyBytesHexFormat(event=None):
if "Hex" in key_input_label_encrypt['text']:
if len(key_input_encrypt.get()) > 32:
key_input_encrypt.delete(32,END)
messagebox.showerror("Key's Length Error", "This key hex value should not be more than 32 bytes.\nLength has been fixed")
key_input_label_encrypt['text'] = "Key (Bts):"
else:
key_input_label_encrypt['text'] = "Key (Bts):"
else:
if len(key_input_encrypt.get()) > 32:
messagebox.showerror("Key's Length Error", "Your key should not be more than 32 bytes.\nLength has been fixed")
key_input_encrypt.delete(32,END)
key_input_label_encrypt['text'] = "Key (Hex):"
else:
key_input_label_encrypt['text'] = "Key (Hex):"
def DecryptKeyBytesHexFormat(event=None):
if "Hex" in key_input_label_decrypt['text']:
if len(key_input_decrypt.get()) > 32:
key_input_decrypt.delete(32,END)
messagebox.showerror("Key's Length Error", "This key hex value should not be more than 32 bytes.\nLength has been fixed")
key_input_label_decrypt['text'] = "Key (Bts):"
else:
key_input_label_decrypt['text'] = "Key (Bts):"
else:
if len(key_input_decrypt.get()) > 32:
messagebox.showerror("Key's Length Error", "Your key should not be more than 32 bytes.\nLength has been fixed")
key_input_decrypt.delete(32,END)
key_input_label_decrypt['text'] = "Key (Hex):"
else:
key_input_label_decrypt['text'] = "Key (Hex):"
def Data_Encrypt(key): # Encrypt Data
try:
cipher = AES.new(key, AES.MODE_EAX)
nonce = cipher.nonce ; Logger( 'imp',"Nonce (hex): " + nonce.hex() ) # sign nonce
Logger('info',"Encrypting with AES-GCM...")
LoadFile.data = pad(LoadFile.data,16) # Fix file bytes length
enc_bytes = cipher.encrypt(LoadFile.data) # Encrypting...
enc_file = open(LoadFile.filepath + '.fcsenc', 'wb') # Make new enc_file
enc_file.write(enc_bytes) # Write encrypted bytes in enc_file
enc_file.close()
if Delete_original_file_checkbox_value.get():
try:
os.remove(LoadFile.filepath) # delete original file
except PermissionError:
Logger('error', "[DE-0] FCS does not have permission to delete original folder. (Encryption Continues)")
enc_file_hash = hashlib.sha256(enc_bytes).hexdigest()
if Backup_keyFile_value.get(): # Option Backup key and nonce to file_keys_backup.txt enabled
with open('信息安全技术/实验三/file_keys_backup.txt', 'a') as enc_file:
try:
if '(Bts)' in key_input_label_encrypt['text']:
enc_file.write(LoadFile.filepath + '.fcsenc' + ' | Hash256: ' + enc_file_hash + " | Key (Bts): "+unpad(key,16).decode('utf-8') + " | " + "Nonce (Hex): " + nonce.hex() + '\n')
else:
enc_file.write(LoadFile.filepath + '.fcsenc' + ' | Hash256: ' + enc_file_hash + " | Key (Hex): " + key.hex() + " | " + "Nonce (Hex): " + nonce.hex() + '\n')
Logger('info',"Key/Nonce have been added in the database.")
except UnicodeEncodeError: # If LoadFile.filepath has unicodes like \u202a, remove them and save decoded filename in db
file = "".join([char for char in LoadFile.filepath if ord(char) < 128])
if '(Bts)' in key_input_label_encrypt['text']:
enc_file.write(file + '.fcsenc' + ' | Hash256: ' + enc_file_hash + " | Key (Bts): "+unpad(key,16).decode('utf-8') + " | " + "Nonce (Hex): " + nonce.hex() + '\n')
else:
enc_file.write(file + '.fcsenc' + ' | Hash256: ' + enc_file_hash + " | Key (Hex): " + key.hex() + " | " + "Nonce (Hex): " + nonce.hex() + '\n')
Logger('info',"Key/Nonce have been added in the database.")
except Exception as e:
Logger('error',"[DE-2] There was an error occured when tried to save key and nonce in databse. Please copy them and save them by hand in a safe place.")
print(e) # For debug purpose
Logger('info',"Encryption Finished.")
Load_Button['text'] = "Load File/Folder"
except ValueError:
Logger('error',"[DE-1] Encryption Failed. Please check your key's length and value")
def AES_Encrypt(): # AES Encrypt
if Load_Button['text'] == "Load File/Folder":
Logger('warn',"Please select a file to encrypt.")
return
elif path.exists(LoadFile.filepath): # file exists
if Load_Type.get() == 1: # If folder has been selected
if FolderToZip():
return
key = key_input_encrypt.get()
if len(key) > 0:
if "Hex" in key_input_label_encrypt['text']:
try:
key = bytes.fromhex(key)
except ValueError:
Logger('warn',"Key's hex value is incorrect.")
return
else:
key = pad(key.encode(),16)
Data_Encrypt(key)
else:
Logger('warn',"Please add a key.")
else:
Logger('error',"[AE-1] File doesn't exist.")
def AES_Decrypt(): # AES Decrypt
if Load_Button['text'] == "Load File/Folder":
Logger('warn',"Please select a file to decrypt.")
elif path.exists(LoadFile.filepath): # file exists
key = key_input_decrypt.get()
nonce = Nonce_input_decrypt.get()
if len(key) > 0 and len(nonce) > 0:
if 'Hex' in key_input_label_decrypt['text']:
try: # unhex key
key = bytes.fromhex(key)
except ValueError:
Logger('error',"[AD-1] Key's hex value is incorrect.")
return
else:
key = pad(key.encode(),16)
try: # unhex nonce
nonce = bytes.fromhex(nonce)
except ValueError:
Logger('error',"[AD-2] Nonce's hex value is incorrect.")
return
if len(key) > 32:
Logger('warn',"Your key must not be more than 32 bytes.")
return
try:
cipher = AES.new(key, AES.MODE_EAX, nonce=nonce)
Logger('info',"Decrypting with AES-GCM...")
try:
decrypted_filename = LoadFile.filepath[:-7] # remove .fcsenc extension
dec_bytes = unpad(cipher.decrypt(LoadFile.data),16)
dec_file = open(decrypted_filename, 'wb')
dec_file.write(dec_bytes)
dec_file.close()
os.remove(LoadFile.filepath) # Delete encrypted file and backup_keys
Logger('info',"Decryption Finished.")
if decrypted_filename[-14:] == '.fcsfolder.zip': # if decrypted file has this extension, then it's zipped folder
shutil.unpack_archive(decrypted_filename, decrypted_filename[:-14], 'zip' ) # unzip
os.remove(decrypted_filename) # delete zip file
Load_Button['text'] = "Load File/Folder"
try: # If key and nonce are saved in database, then after decryption, delete specific line
with open('信息安全技术/实验三/file_keys_backup.txt', 'r') as f:
tmp = ''
for i in f.readlines():
if LoadFile.filepath not in i:
tmp += i
with open('信息安全技术/实验三/file_keys_backup.txt', 'w') as f:
f.write(tmp)
except FileNotFoundError:
Logger('info',"There wasn't found any key/nonce database to remove specific information. (Not Important)")
except ValueError:
Logger('error',"[AD-3] Key or Nonce is not correct.")
except ValueError:
Logger('error',"[AD-4] Key or Nonce is not correct.")
else:
Logger('warn',"Please fill Key and Nonce.")
else:
Logger('error',"[AD-5] File doesn't exist.")
def FileReader(file):
# file = "".join([char for char in file if ord(char) < 128]) # If filename has special unicode characters (chunk), just remove them
try:
LoadFile.data = open(file, 'rb').read() # import data from file.txt
except FileNotFoundError:
Logger('error',"[FR-1] File wasn't found to read it's data. (Encryption/Decryption Stopped)")
return
if LoadFile.name_filename[-7:] == '.fcsenc': # If the file is encrypted, then check the database if it's recorded
QuickDecryptChecker()
if len(LoadFile.full_filename_path) > 47:
LoadFile.full_filename_path = LoadFile.full_filename_path[:25]+".../..."+LoadFile.full_filename_path[len(LoadFile.full_filename_path)-20:]
Load_Button['text'] = LoadFile.full_filename_path # Update LoadFile Button text with loaded file path
def FolderToZip():
Logger('info',"Folder compression is about to start. Please wait...")
try:
Logger('info', "Compressing folder in zip format...")
shutil.make_archive(LoadFile.filepath + '.fcsfolder', 'zip', LoadFile.filepath)
Logger('info', "Compression Finished.")
except FileNotFoundError:
Logger( 'error', "There is no folder with that address: '{}' (Encryption Stopped)".format(LoadFile.filepath) )
return 1
try:
os.remove(LoadFile.filepath) # Delete original folder which got compressed
except PermissionError:
Logger('error', "[FTZ-0] FCS does not have permission to delete original folder. (Encryption Continues)")
LoadFile.filepath = LoadFile.filepath + '.fcsfolder.zip'
FileReader(LoadFile.filepath)
def LoadFile():
if Load_Type.get() == 1: # Load_Type is folder
LoadFile.Load_folder_addr = filedialog.askdirectory(title="Select A Folder")
if LoadFile.Load_folder_addr:
LoadFile.addr_filename, LoadFile.name_filename = os.path.split(LoadFile.Load_folder_addr) # address of directories | filename.*
LoadFile.full_filename_path = LoadFile.addr_filename + "/" + LoadFile.name_filename # example/test/file.txt
LoadFile.filepath = os.path.join(LoadFile.addr_filename, LoadFile.name_filename) # fix filepath format
if len(LoadFile.full_filename_path) > 47:
LoadFile.full_filename_path = LoadFile.full_filename_path[:25]+".../..."+LoadFile.full_filename_path[len(LoadFile.full_filename_path)-20:]
Load_Button['text'] = LoadFile.full_filename_path # Update LoadFile Button text with loaded file path
else: # Load_Type is file
LoadFile.Load_filename_addr = filedialog.askopenfilename(title="Select A File", filetypes=[("All Files", "*.*")] )
if LoadFile.Load_filename_addr: # If user has selected a file
LoadFile.addr_filename, LoadFile.name_filename = os.path.split(LoadFile.Load_filename_addr) # address of directories | filename.*
LoadFile.full_filename_path = LoadFile.addr_filename + "/" + LoadFile.name_filename # example/test/file.txt
LoadFile.filepath = os.path.join(LoadFile.addr_filename, LoadFile.name_filename) # fix filepath format
FileReader(LoadFile.filepath)
def QuickDecryptChecker(): # Check if encrypted file is in database
if path.exists('信息安全技术/实验三/file_keys_backup.txt'):
file_hash = hashlib.sha256(LoadFile.data).hexdigest()
with open('信息安全技术/实验三/file_keys_backup.txt', 'r') as f:
for line in f.readlines():
if file_hash in line:
Logger('info',"This file has been found in the database, so key and nonce have been filled automatically.")
file_items = line.split(' | ')
key = (file_items[2])[11:] # key value
nonce = (file_items[3])[13:-1] # nonce value
if (file_items[2])[:9] == 'Key (Hex)': # if key's format is Hex, then change it in program's settings
key_input_label_decrypt['text'] = "Key (Hex):"
else:
key_input_label_decrypt['text'] = "Key (Bts):"
key_input_decrypt.delete(0,END) # clear decrypt key input
key_input_decrypt.insert(0,key) # fill decrypt key input with key value
Nonce_input_decrypt.delete(0,END) # clear nonce input
Nonce_input_decrypt.insert(0,nonce) # fill nonce input with nonce value
break
###___ Methods ___###
gui = Tk(className='FilesCrypterSavior ' + FCS_Version + '.')
gui.geometry("1000x480")
gui.resizable(False,False)
###--- Default Values ---###
ChangeKeyGenBits.keybytes = 128//8 # Default generate key bytes length
LoadFile.filepath = "-" # Default filepath which does not exist
###___ Default Values ___###
####--- Menu Options ---####
menubar = Menu(gui)
view_menu = Menu(menubar)
###--- Option 0 ---###
view_menu.add_command(label="Check for updates", command=UpdateCheck)
###___ Option 0 ___###
###--- Option 1 ---###
Delete_original_file_checkbox_value = IntVar(value=1)
view_menu.add_checkbutton(label="Delete original file after encryption/decryption", onvalue=1, offvalue=0, variable=Delete_original_file_checkbox_value)
###___ Option 1 ___###
###--- Option 2 ---###
Backup_keyFile_value = IntVar(value=1)
view_menu.add_checkbutton(label="Add key and nonce in the database", onvalue=1, offvalue=0, variable=Backup_keyFile_value)
###___ Option 2 ___###
menubar.add_cascade(label='Options', menu=view_menu)
gui.config(menu=menubar)
####___ Menu Options ___####
row_num = 0
###--- Logo ---###
# photo = PhotoImage(file ="信息安全技术/实验三/logo.png")
# gui.iconphoto(False, photo)
# logo_img = Image.open("信息安全技术/实验三/Letters_logo.png") # logo
# img=ImageTk.PhotoImage(logo_img)
# lab=Label(image=img).grid(row=row_num, columnspan=2, sticky=W)
###___ Logo ___###
row_num += 2
###--- Load Button ---###
Load_Button = Button(gui, text="Load File/Folder", height=2, width=65, command=LoadFile)
Load_Button.grid(row=row_num,column=0,columnspan=2, pady=5, sticky=W)
###___ Load Button ___###
###--- Load Options ---###
Load_Type = IntVar()
R1 = Radiobutton(gui, text="File", variable=Load_Type, value=0)
R1.grid(row=row_num,column=1, sticky=E, ipadx=20)
R2 = Radiobutton(gui, text="Folder", variable=Load_Type, value=1)
R2.grid(row=row_num,column=2, sticky=W)
###___ Load Options ___###
row_num += 1
####--- AES Encrypt ---####
###--- Key Input ---###
key_input_label_encrypt = Label(gui, text="Key (Bts):")
key_input_label_encrypt.grid(row=row_num, column=0, sticky='W')
key_input_label_encrypt.bind('<Button-1>',EncryptKeyBytesHexFormat)
key_input_encrypt = Entry(gui, width=70, borderwidth=1)
key_input_encrypt.grid(row=row_num, column=1, padx=5, pady=40)
###___ Key Input ___###
###--- Generate Key Button ---###
AES_encrypt_random_key_generator_button = Button(gui, text ="Generate 128bits Key (Hex)", height=2, padx=5, fg="green2", bg="black", command=KeyRandomGenerator)
AES_encrypt_random_key_generator_button.grid(row=row_num, column=2, rowspan=2, sticky=W)
AES_encrypt_random_key_generator_button.bind("<Button-3>", ChangeKeyGenBits)
###___ Generate Key Button ___###
AES_encrypt_button = Button(gui, text ="AES Encrypt", height=2, padx=5, fg="green2", bg="black", command=AES_Encrypt).grid(row=row_num, column=3, rowspan=2, sticky=W) #AES Encrypt Button
####___ AES Encrypt ___####
row_num += 2
####--- AES Decrypt ---####
###--- Key Input ---###
key_input_label_decrypt = Label(gui, text="Key (Hex):")
key_input_label_decrypt.grid(row=row_num, column=0, sticky='W')
key_input_decrypt = Entry(gui, width=70, borderwidth=1)
key_input_decrypt.grid(row=row_num, column=1, padx=15)
key_input_label_decrypt.bind('<Button-1>',DecryptKeyBytesHexFormat)
###___ Key Input ___###
###--- Nonce Input ---###
Nonce_input_label = Label(gui, text="Nonce (Hex):", anchor=W).grid(row=row_num+1, column=0)
Nonce_input_decrypt = Entry(gui, width=70, borderwidth=1)
Nonce_input_decrypt.grid(row=row_num+1, column=1, padx=5)
###___ Nonce Input ___###
AES_decrypt_button = Button(gui, text ="AES Decrypt", height=2, padx=5, fg="green2", bg="black", command=AES_Decrypt).grid(row=row_num, column=2, rowspan=2, sticky=W) # AES Decrypt Button
####___ AES Decrypt ___####
row_num += 2
###--- Logger ---###
log_label = Label(gui, text="Log:", anchor=W).grid(row=row_num, column=0, pady=15, sticky='W')
Logging_window = scrolledtext.ScrolledText(gui, width = 100, height = 10)
Logging_window.grid(row=row_num, column=1, columnspan=4, pady=15)
###___ Logger ___###
gui.mainloop()
3.passwords.txt
Password: 123456789
4.requirements.txt
pyperclip
requests
Pillow
pycryptodome
5.README.md
# FilesCrypterSavior
This is a GUI program based on Python3 which helps you encrypt and decrypt files and folders with AES-GCM.
![image](https://user-images.githubusercontent.com/59511698/110169966-245c5f80-7e02-11eb-8875-70c2619a2403.png)
### Tested On
- Windows 10 with Python 3.8.8
- Parrot Linux with Python 3.9.1
## Preparation
- Make sure you support Python 3.X .
- Download the project: `git clone https://github.com/JimChr-R4GN4R/FilesCrypterSavior`
- Then install all required packages by typing in terminal (or cmd):
`python3 -m pip install -r requirements.txt`
- Then get in FilesCrypterSavior's folder and type:
`python3 FilesCrypterSavior.py`
- Done!
## Presentation
[Presentation Video](https://www.youtube.com/watch?v=K3w5Q58m8UA)
## To-Do List
- [X] ~Add `Auto fill Key&Nonce` that checks if the file you want to decrypt exists in database, so it gets key and nonce and fills them in their input automatically.~
- [X] ~Add `Update Checker` option.~
- [X] ~When selecting a folder instead of file, zip it and then encrypt it.~ (It's recommended not to encrypt files more than ~3GB at once if there is not enough space)
- [ ] Add `BKP` (Basic Key Protector) system which means that user enters a key which he wants and database will be encrypted/decrypted by this key and files' keys and nonces will be generated automatically, so user has just to know only one key to encrypt/decrypt his files.
6.file_keys_backup.txt(此文件内容为空,但要有,新建就好)
五、资源下载
下载地址:2022秋季信息安全实验3
到此结束,学Vue去了hhh