이 프로젝트는 RSA(비대칭키)와 AES(대칭키)를 결합한 하이브리드 암호화(Hybrid Encryption) 방식을 사용하여 데이터를 안전하게 전송하는 방법을 보여주는 Python 예제입니다.
'학생'(송신자)이 '교수'(수신자)의 공개키를 사용하여 메시지를 암호화하고, '교수'는 자신의 개인키로만 이 메시지를 복호화할 수 있는 시나리오를 기반으로 합니다.
이 시스템은 RSA의 안전성과 AES의 효율성을 모두 활용합니다.
- AES (대칭키 암호화): 매우 빠른 속도로 대용량 데이터를 암호화하는 데 사용됩니다. 하지만 암/복호화에 동일한 키를 사용하므로, 이 키를 상대방에게 안전하게 전달하는 것이 중요합니다.
- RSA (비대칭키 암호화): 공개키와 개인키 쌍으로 작동합니다. 속도는 느리지만, 공개키로 암호화된 내용은 해당 개인키를 가진 사람만 열어볼 수 있어 키를 안전하게 교환하는 데 적합합니다.
암호화 과정 (학생 측)
- 데이터를 암호화할 **임시 대칭키(AES 세션키)**를 무작위로 생성합니다.
- 이 AES 세션키를 사용해 실제 데이터를 빠르고 효율적으로 암호화합니다.
- 데이터 암호화에 사용된 AES 세션키를 교수의 RSA 공개키로 암호화합니다.
[RSA로 암호화된 세션키]+[암호화된 데이터의 길이]+[AES로 암호화된 데이터]를 하나로 합쳐서 교수에게 전송합니다.
복호화 과정 (교수 측)
- 수신한 데이터에서
[RSA로 암호화된 세션키]부분을 분리합니다. - 자신만 가진 RSA 개인키로 이것을 복호화하여 원본 AES 세션키를 얻습니다.
- 복원된 AES 세션키를 사용하여
[AES로 암호화된 데이터]부분을 복호화하여 원본 데이터를 확인합니다.
.
├── generate_keys.py # RSA 공개키/개인키 쌍을 생성하는 유틸리티
├── crypto.py # 실제 암호화 및 복호화 로직을 담은 라이브러리
├── private_key.pem # 생성된 RSA 개인키 (외부 유출 금지!)
└── public_key.pem # 생성된 RSA 공개키 (데이터 암호화 시 사용)
-
프로젝트 클론
git clone https://github.com/your-username/your-repository-name.git cd your-repository-name -
가상 환경 생성 및 활성화 (권장)
python -m venv venv source venv/bin/activate # macOS/Linux # venv\Scripts\activate # Windows
-
필요한 라이브러리 설치 프로젝트는
cryptography라이브러리를 사용합니다.pip install cryptography
먼저, 암/복호화에 사용할 RSA 키 쌍을 생성해야 합니다. 이 작업은 최초 1회만 수행하면 됩니다.
python generate_keys.py실행 후, 프로젝트 폴더에 private_key.pem(개인키)과 public_key.pem(공개키) 파일이 생성됩니다.
⚠️ 중요:private_key.pem파일은 절대 외부에 공유하거나 유출해서는 안 됩니다. 이 키는 데이터를 복호화할 수 있는 유일한 열쇠입니다.
데이터를 암호화하는 측(학생)은 수신자(교수)의 공개키를 알아야 합니다.
public_key.pem파일을 엽니다.-----BEGIN PUBLIC KEY-----부터-----END PUBLIC KEY-----까지의 모든 내용을 복사합니다.crypto.py파일을 열고,PROFESSOR_PUBLIC_KEY상수의 따옴표 안에 복사한 내용을 붙여넣습니다.
# crypto.py
# ...
PROFESSOR_PUBLIC_KEY = """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApLrm3+PqnidkFIh8BGpY
r1MhGF7MTwzrE5h3wQfx37/X65Tw7qh62D9E3kn7WdW7ETVaKb0nRTv0Ym265Fdv
... (여기에 public_key.pem 내용을 붙여넣으세요) ...
HwIDAQAB
-----END PUBLIC KEY-----
"""
# ...아래 예제 코드를 example.py 와 같이 새로운 파일로 만들어 실행하면 전체 암/복호화 과정을 테스트할 수 있습니다.
example.py
import struct
from cryptography.hazmat.primitives import serialization
from crypto import encrypt_data, decrypt_data, RSA_ENCRYPTED_KEY_SIZE
def load_private_key(filepath: str = "private_key.pem"):
"""PEM 파일에서 개인키를 로드합니다."""
try:
with open(filepath, "rb") as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
)
return private_key
except FileNotFoundError:
print(f"🚫 '{filepath}'를 찾을 수 없습니다. 먼저 generate_keys.py를 실행하세요.")
return None
def main():
# 1. 암호화할 원본 데이터
original_data = "이것은 교수님께 보내는 비밀 메시지입니다. This is a secret message for the professor.".encode('utf-8')
print(f"🔒 원본 데이터: {original_data.decode('utf-8')}\n")
# 2. 데이터 암호화 (학생 측)
encrypted_blob = encrypt_data(original_data)
if not encrypted_blob:
print("암호화에 실패했습니다.")
return
print(f"📦 암호화된 전체 데이터 (Hex): {encrypted_blob.hex()}")
print(f" - 전체 길이: {len(encrypted_blob)} 바이트\n")
# --- 데이터 전송 ---
# 3. 데이터 복호화 (교수 측)
print("--- 수신자 측에서 복호화 시작 ---\n")
# 3-1. 개인키 로드
professor_private_key = load_private_key()
if not professor_private_key:
return
# 3-2. 수신된 데이터 분리
# [RSA암호화된 키][데이터길이][AES암호화된 데이터] 형식으로 구성됨
rsa_key = encrypted_blob[:RSA_ENCRYPTED_KEY_SIZE]
# 데이터 길이를 읽어 AES 데이터 부분을 정확히 분리
aes_data_len_bytes = encrypted_blob[RSA_ENCRYPTED_KEY_SIZE : RSA_ENCRYPTED_KEY_SIZE + 4]
aes_data_len = struct.unpack('>I', aes_data_len_bytes)[0]
aes_data_start = RSA_ENCRYPTED_KEY_SIZE + 4
aes_data = encrypted_blob[aes_data_start : aes_data_start + aes_data_len]
print(f"分解된 정보:")
print(f" - RSA 암호화 키 길이: {len(rsa_key)}")
print(f" - AES 암호화 데이터 길이: {len(aes_data)} (헤더값: {aes_data_len})\n")
# 3-3. 데이터 복호화
decrypted_data = decrypt_data(rsa_key, aes_data, professor_private_key)
if decrypted_data:
print(f"🗝️ 복호화된 데이터: {decrypted_data.decode('utf-8')}\n")
# 3-4. 원본과 비교
assert original_data == decrypted_data
print("✅ 데이터 무결성 확인 완료: 원본과 복호화된 데이터가 일치합니다.")
else:
print("❌ 복호화에 실패했습니다.")
if __name__ == '__main__':
main()예제 실행
python example.py예상 출력 결과
🔒 원본 데이터: 이것은 교수님께 보내는 비밀 메시지입니다. This is a secret message for the professor.
📦 암호화된 전체 데이터 (Hex): ... (긴 16진수 문자열) ...
- 전체 길이: 364 바이트
--- 수신자 측에서 복호화 시작 ---
分解된 정보:
- RSA 암호화 키 길이: 256
- AES 암호화 데이터 길이: 104 (헤더값: 104)
🗝️ 복호화된 데이터: 이것은 교수님께 보내는 비밀 메시지입니다. This is a secret message for the professor.
✅ 데이터 무결성 확인 완료: 원본과 복호화된 데이터가 일치합니다.
이 프로젝트에서 사용되는 두 종류의 키(RSA, AES)는 생성 시점과 생명 주기가 완전히 다릅니다. 이 차이를 이해하는 것은 하이브리드 암호화 시스템의 보안을 이해하는 데 매우 중요합니다.
-
언제 생성되나요?
- 사용자가
generate_keys.py를 수동으로 실행할 때 단 한 번 생성됩니다.
- 사용자가
-
매번 같은 값인가요?
- 아니요.
generate_keys.py를 실행할 때마다 암호학적으로 안전한 난수를 기반으로 항상 새롭고 예측 불가능한 키 쌍이 만들어집니다.
- 아니요.
-
어떻게 관리해야 하나요?
- 이 키 쌍은 장기적으로 사용하는 것을 전제로 합니다. 수신자(교수)의 '신원'과 같아서, 한번 생성하면 키가 유출되지 않는 한 계속 사용합니다.
- 만약 키를 변경하고 싶다면,
generate_keys.py를 다시 실행하고, 생성된 새로운public_key.pem의 내용을 반드시crypto.py에 업데이트해야 합니다. 그렇지 않으면 복호화가 불가능합니다.
비유:
generate_keys.py는 은행 금고와 그 금고의 마스터키를 만드는 과정과 같습니다. 한번 만들면 그 금고와 열쇠는 계속 사용합니다. 보안 문제(키 유출 등)가 생기면 아예 새로운 금고와 열쇠 세트로 교체해야 합니다.
-
언제 생성되나요?
crypto.py의encrypt_data()함수가 호출될 때마다 매번 자동으로 생성됩니다.
-
사용자가 설정할 필요가 있나요?
- 아니요, 전혀 없습니다. 프로그램이 암호화할 때마다 내부적으로 항상 다른, 새로운 키를 만들도록 완벽하게 자동화되어 있습니다.
os.urandom()함수가 매번 예측 불가능한 난수를 생성하여 키를 만들기 때문입니다.
-
왜 이렇게 하나요? (전방향 안전성, Forward Secrecy)
- 이 방식은 보안을 극대화합니다. 각 메시지가 고유한 일회용 키로 암호화되므로, 만약 공격자가 과거에 전송된 메시지 하나를 해독하는 데 성공하더라도, 그 정보는 오직 그 메시지 하나에만 유효합니다. 다른 메시지들(과거 또는 미래의)은 전혀 다른 키로 암호화되었기 때문에 안전하게 유지됩니다.
비유: 데이터를 보낼 때마다 매번 새로운 택배 상자와 새로운 일회용 자물쇠를 사용하는 것과 같습니다. 내용물을 상자에 넣고 자물쇠로 잠근 뒤, 그 자물쇠 열쇠를 받는 사람의 개인 금고(RSA 공개키)에만 넣을 수 있는 안전한 캡슐에 담아 함께 보냅니다. 다음 데이터를 보낼 땐 또 다른 상자와 자물쇠를 사용합니다.