IT 관련/유틸리티

Python 암호화 라이브러리 이용 예제

islet2 2025. 2. 19. 21:31

crypto.py
0.01MB

파일 암호화/복호화 프로그램 설명

이 프로그램은 중요한 텍스트 파일을 비밀번호로 보호할 수 있는 간단한 도구입니다. 마치 귀중한 보물상자에 열쇠를 채우는 것처럼, 여러분의 중요한 문서에 '디지털 자물쇠'를 채울 수 있습니다.

프로그램의 주요 기능

  1. 텍스트 작성 및 편집: 메모장처럼 텍스트를 자유롭게 작성하고 편집하실 수 있습니다.
  2. 암호화 저장: 작성한 내용을 4~10자리 비밀번호로 암호화하여 저장합니다. 이렇게 하면 파일을 열어도 내용을 읽을 수 없게 됩니다.
  3. 복호화 열기: 암호화된 파일을 다시 열 때는 동일한 비밀번호를 입력해야만 내용을 볼 수 있습니다.

비전문가를 위한 기술적 요소 설명

  1. 비밀번호 처리 방식:
    • 입력하신 비밀번호는 그대로 저장되지 않고, '해시'라는 특별한 방식으로 변환됩니다.
    • 이는 마치 비밀번호를 특별한 암호문으로 바꾸는 것과 같습니다.
  2. 암호화 과정:
    • 파일 내용을 '암호화 열쇠'로 잠그는 과정입니다.
    • 추가로 'HMAC'이라는 디지털 서명을 함께 저장하여 파일이 나중에 변조되었는지 확인할 수 있습니다.
  3. 복호화 과정:
    • 암호화된 파일을 열 때, 먼저 디지털 서명을 확인합니다.
    • 서명이 일치하면 비밀번호로 내용을 복원합니다.
    • 잘못된 비밀번호를 입력하거나 파일이 손상되었다면 복호화에 실패합니다.

안전성 특징

  1. 비밀번호 보호: 화면에 입력할 때 "*"로 표시되어 다른 사람에게 보이지 않습니다.
  2. 변조 방지: 암호화된 파일이 누군가에 의해 변경되었다면, 복호화 과정에서 이를 감지하고 경고합니다.
  3. 간단한 사용법: 복잡한 기술 지식 없이도 '파일' 메뉴에서 저장/불러오기만 하면 됩니다.

사용 시 주의사항

  1. 비밀번호는 반드시 기억해두세요. 잊어버리면 파일을 복구할 수 없습니다.
  2. 4~10자 사이의 비밀번호를 사용하셔야 합니다.
  3. 암호화된 파일은 일반 텍스트 편집기로 열어도 내용을 확인할 수 없습니다.

이 프로그램은 개인 일기, 중요한 메모, 비밀 정보 등을 안전하게 보관하고 싶으신 분들께 유용합니다. 복잡한 보안 소프트웨어 없이도 간단히 파일을 보호하실 수 있습니다.

 

import tkinter as tk
from tkinter import filedialog, messagebox
from cryptography.fernet import Fernet
import hashlib
import hmac
import base64

def generate_key(password):
    """
    사용자 비밀번호로부터 Fernet 키를 안전하게 생성합니다.
    """
    # 비밀번호를 바이트로 인코딩합니다.
    password_bytes = password.encode('utf-8')

    # SHA256 해시 함수를 사용하여 비밀번호를 해싱합니다.
    hashed_password = hashlib.sha256(password_bytes).digest()

    # 해시된 비밀번호를 Base64로 인코딩하여 Fernet 키로 사용합니다.
    # Fernet 키는 정확히 32바이트여야 하며, Base64로 인코딩하면 44바이트가 됩니다.
    key = base64.urlsafe_b64encode(hashed_password)

    return key


def encrypt_file(filename, password):
    """파일을 암호화하고 HMAC을 추가합니다."""
    try:
        key = generate_key(password)
        f = Fernet(key)

        with open(filename, "rb") as file:
            file_data = file.read()

        encrypted_data = f.encrypt(file_data)

        # HMAC 생성
        hmac_obj = hmac.new(key, encrypted_data, hashlib.sha256)
        hmac_value = hmac_obj.digest()

        # HMAC과 암호화된 데이터를 함께 저장
        with open(filename, "wb") as file:
            file.write(hmac_value + encrypted_data)  # HMAC을 먼저 쓰고, 그 다음에 암호화된 데이터

        messagebox.showinfo("성공", "파일이 암호화되었습니다.")

    except Exception as e:
        messagebox.showerror("오류", f"암호화 중 오류 발생: {e}")

def decrypt_file(filename, password):
    """파일을 복호화하고 HMAC을 검증합니다."""
    try:
        key = generate_key(password)
        f = Fernet(key)

        with open(filename, "rb") as file:
            combined_data = file.read()

        hmac_value = combined_data[:32]  # HMAC은 SHA256이므로 32바이트
        encrypted_data = combined_data[32:]

        # HMAC 검증
        hmac_obj = hmac.new(key, encrypted_data, hashlib.sha256)
        expected_hmac = hmac_obj.digest()

        if not hmac.compare_digest(hmac_value, expected_hmac):  # 중요: compare_digest 사용
            raise ValueError("복호화 실패: 잘못된 암호키 또는 파일 손상")  # 오류 발생

        decrypted_data = f.decrypt(encrypted_data)

        with open(filename, "wb") as file:
            file.write(decrypted_data)

        messagebox.showinfo("성공", "파일이 복호화되었습니다.")

    except ValueError as ve: # HMAC 검증 실패
        messagebox.showerror("오류", f"복호화 실패: 잘못된 암호키 또는 파일 손상")
    except Exception as e:
        messagebox.showerror("오류", f"복호화 중 오류 발생: {e}")


def save_file_with_encryption():
    """파일 저장 및 암호화 GUI."""
    def save_and_encrypt():
        filename = filedialog.asksaveasfilename(defaultextension=".txt")  # 원하는 확장자로 변경 가능
        if not filename:
            return

        password = password_entry.get()
        if not (4 <= len(password) <= 10):
            messagebox.showerror("오류", "암호키는 4~10자 사이여야 합니다.")
            return

        try:
            # 파일 내용을 가져오는 로직 (예시)
            file_content = text_editor.get("1.0", tk.END)
            with open(filename, "w", encoding="utf-8") as file:
                file.write(file_content) # 파일에 내용 저장

            encrypt_file(filename, password)
            save_dialog.destroy()

        except Exception as e:
            messagebox.showerror("오류", f"파일 저장 중 오류 발생: {e}")

    # 저장 다이얼로그 생성
    save_dialog = tk.Toplevel(root)
    save_dialog.title("파일 저장 및 암호화")

    password_label = tk.Label(save_dialog, text="암호키 (4~10자):")
    password_label.pack()
    password_entry = tk.Entry(save_dialog, show="*")  # 암호 숨김
    password_entry.pack()

    save_button = tk.Button(save_dialog, text="저장 및 암호화", command=save_and_encrypt)
    save_button.pack()

def load_file_with_decryption():
    """파일 로드 및 복호화 GUI."""
    def load_and_decrypt():
        filename = filedialog.askopenfilename()
        if not filename:
            return

        password = password_entry.get()
        if not (4 <= len(password) <= 10):
            messagebox.showerror("오류", "암호키는 4~10자 사이여야 합니다.")
            return

        try:
            decrypt_file(filename, password)

            # 복호화된 파일 내용을 텍스트 에디터에 표시
            with open(filename, "r", encoding="utf-8") as file:
                decrypted_content = file.read()
                text_editor.delete("1.0", tk.END)  # 기존 내용 삭제
                text_editor.insert(tk.END, decrypted_content)

            load_dialog.destroy()

        except Exception as e:
            messagebox.showerror("오류", f"파일 로드 중 오류 발생: {e}")

    # 로드 다이얼로그 생성
    load_dialog = tk.Toplevel(root)
    load_dialog.title("파일 로드 및 복호화")

    password_label = tk.Label(load_dialog, text="암호키 (4~10자):")
    password_label.pack()
    password_entry = tk.Entry(load_dialog, show="*")  # 암호 숨김
    password_entry.pack()

    load_button = tk.Button(load_dialog, text="로드 및 복호화", command=load_and_decrypt)
    load_button.pack()

# 메인 윈도우
root = tk.Tk()
root.title("파일 암호화/복호화")

# 텍스트 에디터 추가
text_editor = tk.Text(root)
text_editor.pack(expand=True, fill="both")


# 메뉴 바 생성
menu_bar = tk.Menu(root)
file_menu = tk.Menu(menu_bar, tearoff=0)
file_menu.add_command(label="저장 및 암호화", command=save_file_with_encryption)
file_menu.add_command(label="불러오기 및 복호화", command=load_file_with_decryption)
file_menu.add_separator()
file_menu.add_command(label="종료", command=root.quit)
menu_bar.add_cascade(label="파일", menu=file_menu)
root.config(menu=menu_bar)


root.mainloop()