파이썬이 제일 쉬워

[Python] Pycrypto로 AES 개날먹 암호화 하기 (+ 패딩(padding) 개쉽게하는 법) 본문

Python

[Python] Pycrypto로 AES 개날먹 암호화 하기 (+ 패딩(padding) 개쉽게하는 법)

HighBright 2024. 6. 12. 23:40

 

반갑꼬리~ 오늘은 매우 간단하게 Pycrypto로 AES 암/복호화를 해볼겁니다.

정.말.간.단.합.니.다. 10줄 안으로 끝남 ㅇㅇ

패딩 날먹 바로보기

 

먼저 pycrypto를 설치해줍시다.

pip install pycrypto

자 이제 틀부터 잡아봅시다.

파일을 AES로 암호화 하려면 일단 필수적으로 두가지가 필요합니다.

암호화할 파일과, 키(Key)죠.

(근데, 우리는 CBC 방식으로 암호화 할거기 때문에 추가적으로 초기화 벡터(iv)가 필요합니다.)

 

AES 암호화에 대한 자세한 설명은 여기 참조 https://blog.naver.com/sanainfo/221517009223

 

 
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
 
def enc_file(input_file, key): # 캬~ 10줄 컷 / 더 줄일 수 있긴한데 가독성 땜에 냅둠
    iv = get_random_bytes(16)
    key = pad(key, 32) # key 패딩, AES256(32bytes)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    with open(input_file, 'rb') as f:
        plain = f.read()
    padded_plain = pad(plain, AES.block_size)
    ciphertext = cipher.encrypt(padded_plain)
    with open(input_file + ".aes", 'wb') as f:
        f.write(iv)
        f.write(ciphertext)
 

 

이게 끝임. 진짜로.

인터넷 찾아보면 패딩을 뭐 존나 어렵게 하던데 걍 일케하면 댐 ㅇㅇ

 

이제 설명을 해보자면

iv = get_random_bytes(16) => 16바이트 짜리 랜덤한 바이트를 생성해서 초기화 벡터로 삼는다.

key = pad(key, 32) 

=> key도 패딩해준다 AES256이라 32바이트에 맞춰줌

(AES128은 16bytes, AES192는 24bytes, AES256은 32bytes임)

AES128 할 때는 저기에 밑에 처럼 AES.block_size 써도 됨 얘도 걍 int 16임

패딩 개같이 복잡하게 하는 사람들 많은데 걍 이거 쓰면 끝임

 

cipher = AES.new(key, AES.MODE_CBC, iv)

=> 새로운 모듈(?)을 하나 만들어준다. key는 암호화에 사용할 키

AES.MODE_CBC는 CBC모드로 암호화 함을 의미하고, iv는 초기화 벡터를 입력해준 것.

 

with open(input_file, 'rb') as f:

    plain = f.read()

=> 파일을 읽어온다 'rb'를 통해 바이트로 읽어와야 함 ㅇㅇ

 

padded_plain = pad(plain, AES.block_size)

=> block_size로 읽어온 내용을 패딩 해준다 (block_size는 16바이트임)

 

ciphertext = cipher.encrypt(padded_plain)

=> 패딩된 내용을 암호화한다.

 

with open(input_file + ".aes", 'wb) as f:

    f.write(iv) 

    f.write(ciphertext)

=> 암호화된 내용을 파일에 저장한다. 

 

# 초기화 벡터가 2번째 키의 역할을 하기도 하는데, 걍 써버려도 되는건가?

운용 방식마다 초기화 벡터를 사용하는 방법이 다르며 초기화 벡터에서 요구되는 성질도 조금씩 다를 수 있지만, 같은 초기화 벡터가 반복되어 사용되어서는 안 된다는 성질을 공통적으로 가진다. 이것은 초기화 벡터가 같은 경우 비슷한 두 개의 평문을 암호화했을 때 앞부분의 블록들이 서로 같게 되는 등 보안 문제가 발생하기 때문이다. (위키피디아)

 

사실 이걸 쓰는 이유가 초기화 벡터를 추가해서 비슷한 문장 암호화시에 비슷한 결과값이 나오는 걸 방지하는 거라 크게 문제는 안됨. key만 유출안되게 관리 잘하면됨 ㅇㅇ.

 

사실상 이걸로 "모든 파일"을 암호화 할 수 있다.

 

근데....  이렇게 하면 귀찮죠? 간단한 GUI도 끼워넣어 봅시당.간단한 GUI에 디자인은 필요없기 때문에 tkinter 쓸겁니다. (진짜 사용하는건 customtkinter로 이쁘게 만듦)

 

전체 코드

 
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import tkinter as tk
from tkinter import filedialog, messagebox

def enc_file(input_file, key):
    iv = get_random_bytes(16)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    with open(input_file, 'rb') as f:
        plaintext = f.read()
    padded_plaintext = pad(plaintext, AES.block_size)
    ciphertext = cipher.encrypt(padded_plaintext)
    with open(input_file+".aes", 'wb') as f:
        f.write(iv)
        f.write(ciphertext)

def adjust_key_length(key):
    if len(key) > 32:     # 키가 32바이트보다 길 경우 자르기
        return key[:32]
elif len(key) < 32:     # 키가 32바이트보다 짧을 경우 패딩하기
        padding = b'\0' * (16 - len(key))  # 널(Null)로 패딩
        return key + padding
    else:     # 키가 32바이트일 경우 그대로 반환
        return key

       
def select_file():
    file_path = filedialog.askopenfilename()
    file_entry.delete(0, tk.END)
    file_entry.insert(0, file_path)

def start_enc():
    input_file = file_entry.get()
    key = key_entry.get().encode()
    key = adjust_key_length(key)
    if not input_file or not key:
        messagebox.showerror("에러", "파일이나 키가 비어있습니다. 입력해주세요")
        return
   
    try:
        enc_file(input_file, key)
        messagebox.showinfo("성공", "암호화 성공")
    except Exception as e:
        messagebox.showerror("에러", f"에러 발생 : {e}")
       
def check_key_len(*args):
    key_len.configure(text=str(len(key_var.get()))+" Bytes")
   
root = tk.Tk()
root.title("AES 암호화")

file_label = tk.Label(root, text="파일 주소 :")
file_label.grid(row=0, column=0, padx=10, pady=10)

file_entry = tk.Entry(root, width=50)
file_entry.grid(row=0, column=1, padx=10, pady=10)

file_button = tk.Button(root, text="파일 선택", command=select_file)
file_button.grid(row=0, column=2, padx=10, pady=10)



key_label = tk.Label(root, text="키 :")
key_label.grid(row=1, column=0, padx=10, pady=10)

key_var = tk.StringVar()
key_var.trace_add('write', check_key_len)
key_entry = tk.Entry(root, width=50, textvariable=key_var)
key_entry.grid(row=1, column=1, padx=10, pady=10)

key_len = tk.Label(root, text="0 Bytes")
key_len.grid(row=1, column=2, padx=10, pady=10)


encrypt_button = tk.Button(root, text="암호화", command=start_enc)
encrypt_button.grid(row=2, column=0, columnspan=3, padx=10, pady=20)

root.mainloop()
 

 

쟈쟈쟌~

 


복호화는 담시간에 합시당

Comments