TL;DR
- AES-GCM์ Fernet๋ณด๋ค ์ ์ฐํ ๋์นญ ํค ์ํธํ, RSA๋ ๊ณต๊ฐ ํค/๊ฐ์ธ ํค ์์ ๋น๋์นญ ์ํธํ
- ์ค๋ฌด์์๋ ๋ฐ์ดํฐ๋ AES๋ก, AES ํค๋ RSA๋ก ์ํธํํ๋ ํ์ด๋ธ๋ฆฌ๋ ๋ฐฉ์ ์ฌ์ฉ
- ํด์ฑ์ ๋จ๋ฐฉํฅ ๋ณํ์ผ๋ก ๋น๋ฐ๋ฒํธ ์ ์ฅ ๋ฑ์ ์ฌ์ฉ, bcrypt/argon2 ๊ถ์ฅ
AI-assisted
์ด์ ๊ธ
Python cryptography 1 - Fernet์์ Fernet ๊ธฐ๋ฐ ๋์นญ ํค ์ํธํ๋ฅผ ๋ค๋ค๋ค. ์ด ๊ธ์์๋ Fernet์ผ๋ก ํด๊ฒฐํ๊ธฐ ์ด๋ ค์ด ์ํฉ์์ ์ฌ์ฉํ ์ ์๋ ์ ์์ค API๋ฅผ ๋ค๋ฃฌ๋ค.
1. AES ์ง์ ์ฌ์ฉํ๊ธฐ
Fernet์ ๋ด๋ถ์ ์ผ๋ก AES-128-CBC๋ฅผ ์ฌ์ฉํ์ง๋ง, ํค ๊ธธ์ด๋ ๋ชจ๋๋ฅผ ๋ฐ๊ฟ ์ ์๋ค. cryptography์ hazmat ๋ ์ด์ด๋ฅผ ์ฌ์ฉํ๋ฉด AES๋ฅผ ์ง์ ์ ์ดํ ์ ์๋ค.
AES-GCM์ด๋
AES-GCM(Galois/Counter Mode)์ ํ์ฌ ๊ฐ์ฅ ๋๋ฆฌ ๊ถ์ฅ๋๋ ๋์นญ ํค ์ํธํ ๋ชจ๋๋ค.
- ์ํธํ + ์ธ์ฆ์ ํ๋ฒ์ ์ฒ๋ฆฌํ๋ค (Authenticated Encryption)
- ๋ณ๋์ HMAC ๊ณ์ฐ์ด ํ์ ์๋ค
- AES-256 ๋ฑ ๋ ๊ธด ํค๋ฅผ ์ฌ์ฉํ ์ ์๋ค
- ์คํธ๋ฆฌ๋ฐ/์ฒญํฌ ๋จ์ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
# 1. ํค ์์ฑ (256๋นํธ = 32๋ฐ์ดํธ)
key = AESGCM.generate_key(bit_length=256)
# 2. nonce ์์ฑ (12๋ฐ์ดํธ ๊ถ์ฅ)
nonce = os.urandom(12)
# 3. ์ํธํ
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, b"secret data", None)
# 4. ๋ณตํธํ
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
print(plaintext) # b'secret data'nonce๋ ์ ๋ ์ฌ์ฌ์ฉํ๋ฉด ์ ๋๋ค
๊ฐ์ ํค๋ก ๊ฐ์ nonce๋ฅผ ๋ ๋ฒ ์ฌ์ฉํ๋ฉด ์ํธํ๊ฐ ๊นจ์ง๋ค. ๋งค ์ํธํ๋ง๋ค
os.urandom(12)๋ก ์๋ก ์์ฑํ๊ณ , ciphertext์ ํจ๊ป ์ ์ฅํด์ผ ํ๋ค.
AAD (Associated Authenticated Data)
encrypt์ ์ธ ๋ฒ์งธ ์ธ์๋ AAD๋ค. ์ํธํํ์ง๋ ์์ง๋ง ๋ฌด๊ฒฐ์ฑ์ ๊ฒ์ฆํ๊ณ ์ถ์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๋๋ค.
# ํค๋ ์ ๋ณด๋ ์ํธํํ์ง ์๋, ๋ณ์กฐ ์ฌ๋ถ๋ ๊ฒ์ฆ
aad = b"user_id=42"
ciphertext = aesgcm.encrypt(nonce, b"secret data", aad)
# ๋ณตํธํ ์ ๊ฐ์ AAD๋ฅผ ๋๊ฒจ์ผ ์ฑ๊ณต
plaintext = aesgcm.decrypt(nonce, ciphertext, aad)
# AAD๊ฐ ๋ค๋ฅด๋ฉด InvalidTag ์์ธ
aesgcm.decrypt(nonce, ciphertext, b"user_id=99")
# cryptography.exceptions.InvalidTagFernet vs AES-GCM ๋น๊ต
| ํญ๋ชฉ | Fernet | AES-GCM |
|---|---|---|
| ๋์ด๋ | ์ฌ์ | ์ค๊ฐ |
| ํค ๊ธธ์ด | 128๋นํธ ๊ณ ์ | 128/192/256๋นํธ ์ ํ |
| ๋ชจ๋ | CBC + HMAC | GCM (์ธ์ฆ ๋ด์ฅ) |
| nonce/IV ๊ด๋ฆฌ | ์๋ | ์ง์ ๊ด๋ฆฌ |
| ๋์ฉ๋ ํ์ผ | ๋ถ์ ํฉ | ์ ํฉ |
| ์ฉ๋ | ๊ฐ๋จํ ๋ฐ์ดํฐ ์ํธํ | ์ธ๋ฐํ ์ ์ด๊ฐ ํ์ํ ๋ |
2. ๋น๋์นญ ํค ์ํธํ (RSA)
๋์นญ ํค๊ฐ ๊ฐ์ ์ด์ ๋ก ์ ๊ทธ๊ณ ์ฌ๋ ๊ธ๊ณ ์๋ค๋ฉด, RSA๋ ์ฐ์ฒดํต๊ณผ ๊ฐ๋ค. ๋๊ตฌ๋ ์ฐํธ๋ฌผ์ ๋ฃ์ ์ ์์ง๋ง(๊ณต๊ฐ ํค๋ก ์ํธํ), ์ด์ด์ ๊บผ๋ด๋ ๊ฒ์ ์ด์ ๋ฅผ ๊ฐ์ง ์ฃผ์ธ๋ง ๊ฐ๋ฅํ๋ค(๊ฐ์ธ ํค๋ก ๋ณตํธํ).
- ๊ณต๊ฐ ํค : ๋๊ตฌ์๊ฒ๋ ๊ณต๊ฐ. ์ํธํ์ ์ฌ์ฉ
- ๊ฐ์ธ ํค : ๋ณธ์ธ๋ง ๋ณด์ . ๋ณตํธํ์ ์ฌ์ฉ
RSA ํค์ ์ํ์ ์๋ฆฌ
RSA ํค๋ ๋ค์ ๊ฐ๋ค๋ก ๊ตฌ์ฑ๋๋ค. ๊ฐ ๊ฐ์ด ์ด๋ป๊ฒ ๊ฒฐ์ ๋๋์ง ์์ ์ซ์ ์์๋ก ์ดํด๋ณด์.
p, q โ ๊ฐ์ธํค๋ฅผ ๋ง๋ค ์ฌ๋์ด ๋๋คํ๊ฒ ๊ณ ๋ฅด๋ ํฐ ์์
n โ p ร q ๋ก ๊ณ์ฐ๋จ
ฯ(n) โ (p - 1) ร (q - 1) ๋ก ๊ณ์ฐ๋จ
e โ ฯ(n)๊ณผ ์๋ก์์ธ ๊ณต๊ฐ ์ง์. ๋ณดํต 65537์ ์ฌ์ฉ
d โ e ร d โก 1 mod ฯ(n) ์ ๋ง์กฑํ๋ ๋ชจ๋๋ฌ ์ญ์p = 61, q = 53์ผ๋ก ์์๋ฅผ ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
n = 61 ร 53 = 3233
ฯ(n) = 60 ร 52 = 3120
e = 17 ์ ํ (gcd(17, 3120) = 1 ์ด๋ฏ๋ก ๊ฐ๋ฅ)
d = ?
17 ร d โก 1 mod 3120 ์ ๋ง์กฑํ๋ ๊ฐ
17 ร 2753 = 46801 = 1 + 15 ร 3120
โ d = 2753
๊ณต๊ฐํค = (n=3233, e=17)
๊ฐ์ธํค = (n=3233, d=2753)| ๊ฐ | ์๋ฏธ | ๊ณต๊ฐ ์ฌ๋ถ | ๊ฒฐ์ ๋ฐฉ์ |
|---|---|---|---|
| p, q | ํฐ ์์ | ๋น๋ฐ | ๋๋ค ์์ฑ |
| n | p ร q | ๊ณต๊ฐ | ๊ณ์ฐ |
| ฯ(n) | (p-1)(q-1) | ๋น๋ฐ | ๊ณ์ฐ |
| e | ๊ณต๊ฐ ์ง์ | ๊ณต๊ฐ | ์กฐ๊ฑด ๋ง์กฑํ๋ ๊ฐ ์ ํ |
| d | ๊ฐ์ธ ์ง์ | ๋น๋ฐ | e์ ๋ชจ๋๋ฌ ์ญ์์ผ๋ก ๊ณ์ฐ |
์ํธํ์ ๋ณตํธํ๋ ์ด ๊ด๊ณ๋ฅผ ์ด์ฉํ๋ค.
์ํธํ: C = M^e mod n
๋ณตํธํ: M = C^d mod ne ร d โก 1 mod ฯ(n) ์ด๋ผ๋ ๊ด๊ณ ๋๋ถ์ M^(eรd) mod n = M์ด ์ฑ๋ฆฝํ๊ณ , ์ํธํํ ๊ฒ์ ๋ณตํธํํ๋ฉด ์๋ณธ์ด ๋ณต์๋๋ค.
Python์ผ๋ก ์ง์ ํ์ธํ ์ ์๋ค.
import math
p, q = 61, 53
n = p * q
phi = (p - 1) * (q - 1)
e = 17
print(f"gcd(e, phi): {math.gcd(e, phi)}") # 1
# Python 3.8+์์ ๋ชจ๋๋ฌ ์ญ์ ๊ณ์ฐ
d = pow(e, -1, phi)
print(f"d: {d}") # 2753
print(f"e*d mod phi: {(e * d) % phi}") # 1
# ์ํธํ / ๋ณตํธํ
message = 65
ciphertext = pow(message, e, n) # 2790
decrypted = pow(ciphertext, d, n) # 65
print(f"message: {message}, ciphertext: {ciphertext}, decrypted: {decrypted}")๊ณต๊ฐํค๋ก ๋ณตํธํํ ์ ์๋ ์ด์
๊ณต๊ฐํค์๋ (n, e)๋ง ์๊ณ d๋ ์๋ค. d๋ฅผ ์์๋ด๋ ค๋ฉด ฯ(n)์ด ํ์ํ๊ณ , ฯ(n)์ ๊ณ์ฐํ๋ ค๋ฉด n์ ์์ธ์๋ถํดํด์ p, q๋ฅผ ์์์ผ ํ๋ค.
n = 3233 โ 61 ร 53 (์์ ์ซ์๋ ์ฝ๊ฒ ๋ถํด๋จ)์ค์ RSA-2048์์๋ n์ด 617์๋ฆฌ ์ ๋์ ์ซ์๋ค. ์ด ํฌ๊ธฐ์ ์์ธ์๋ถํด๋ ํ์ฌ ์ปดํจํ
๋ฅ๋ ฅ์ผ๋ก ํ์ค์ ์ผ๋ก ๋ถ๊ฐ๋ฅํ๋ค. ๊ทธ๋์ ๊ณต๊ฐํค๋ง์ผ๋ก๋ ๊ฐ์ธํค๋ฅผ ์์๋ผ ์ ์๋ค.
ํค ์ ์์ฑ
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes, serialization
# ๊ฐ์ธ ํค ์์ฑ
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
# ๊ณต๊ฐ ํค ์ถ์ถ
public_key = private_key.public_key()ํค๋ฅผ ํ์ผ๋ก ์ ์ฅ/๋ถ๋ฌ์ค๊ธฐ
# ๊ฐ์ธ ํค ์ ์ฅ (PEM ํ์)
pem_private = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(b"passphrase"),
)
Path("private.pem").write_bytes(pem_private)
# ๊ณต๊ฐ ํค ์ ์ฅ
pem_public = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
Path("public.pem").write_bytes(pem_public)
# ๊ฐ์ธ ํค ๋ถ๋ฌ์ค๊ธฐ
loaded_private = serialization.load_pem_private_key(
Path("private.pem").read_bytes(),
password=b"passphrase",
)
# ๊ณต๊ฐ ํค ๋ถ๋ฌ์ค๊ธฐ
loaded_public = serialization.load_pem_public_key(
Path("public.pem").read_bytes(),
)์ํธํ / ๋ณตํธํ
# ๊ณต๊ฐ ํค๋ก ์ํธํ
ciphertext = public_key.encrypt(
b"secret message",
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
# ๊ฐ์ธ ํค๋ก ๋ณตํธํ
plaintext = private_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print(plaintext) # b'secret message'RSA๋ ์์ ๋ฐ์ดํฐ๋ง ์ํธํํ ์ ์๋ค
RSA-2048 ๊ธฐ์ค ์ต๋ ์ฝ 190๋ฐ์ดํธ๊น์ง๋ง ์ํธํ ๊ฐ๋ฅํ๋ค. ํฐ ๋ฐ์ดํฐ๋ AES๋ก ์ํธํํ๊ณ , AES ํค๋ง RSA๋ก ์ํธํํ๋ ํ์ด๋ธ๋ฆฌ๋ ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
๋์งํธ ์๋ช / ๊ฒ์ฆ
์ํธํ์ ๋ฐ๋ ๋ฐฉํฅ์ด๋ค. ๊ฐ์ธ ํค๋ก ์๋ช ํ๊ณ ๊ณต๊ฐ ํค๋ก ๊ฒ์ฆํ๋ค. ๋ฐ์ดํฐ๊ฐ ๋ณ์กฐ๋์ง ์์์์ ์ฆ๋ช ํ ๋ ์ฌ์ฉํ๋ค. ์ค์ ๋ก TLS handshake์์ ์๋ฒ ์ธ์ฆ์ ์ด ๋ฐฉ์์ด ์ฐ์ธ๋ค.
from cryptography.hazmat.primitives.asymmetric import utils
message = b"this data must not be tampered"
# ๊ฐ์ธ ํค๋ก ์๋ช
signature = private_key.sign(
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
),
hashes.SHA256(),
)
# ๊ณต๊ฐ ํค๋ก ๊ฒ์ฆ (๋ณ์กฐ๋์ง ์์์ผ๋ฉด ์ ์ ํต๊ณผ)
public_key.verify(
signature,
message,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
),
hashes.SHA256(),
)
# ์์ธ๊ฐ ๋ฐ์ํ์ง ์์ผ๋ฉด ๊ฒ์ฆ ์ฑ๊ณต
# ๋ฉ์์ง๊ฐ ๋ณ์กฐ๋ ๊ฒฝ์ฐ
public_key.verify(signature, b"tampered data", ...)
# cryptography.exceptions.InvalidSignature3. ํ์ด๋ธ๋ฆฌ๋ ์ํธํ
์ RSA๋ก ๋ฐ์ดํฐ ์ ์ฒด๋ฅผ ์ํธํํ์ง ์๋๊ฐ
RSA๋ ๋ ๊ฐ์ง ๋จ์ ์ด ์๋ค.
- ๋๋ฆฌ๋ค : ๋์นญ ํค ์ํธํ๋ณด๋ค ์๋ฐฑ ๋ฐฐ ๋๋ฆฌ๋ค
- ํฌ๊ธฐ ์ ํ์ด ์๋ค : RSA-2048 + OAEP ๊ธฐ์ค ํ ๋ฒ์ ์ฝ 190๋ฐ์ดํธ๊น์ง๋ง ์ํธํ ๊ฐ๋ฅํ๋ค
์ค๋ฌด์์ ๋ค๋ฃจ๋ ๋ฐ์ดํฐ๋ ๋ณดํต ์ด๋ณด๋ค ํจ์ฌ ํฌ๋ค. ๋ฐ๋ฉด AES ๋์นญ ํค๋ 16~32๋ฐ์ดํธ๋ก, RSA๋ก ์ํธํํ๊ธฐ์ ์ถฉ๋ถํ ์๋ค. ๊ทธ๋์ ์ค๋ฌด์์๋ ์ด๋ ๊ฒ ์ญํ ์ ๋๋๋ค.
ํฐ ๋ฐ์ดํฐ โ AES ๊ฐ์ ๋์นญํค๋ก ์ํธํ (๋น ๋ฆ)
AES ํค ์์ฒด โ RSA ๊ฐ์ ๋น๋์นญํค๋ก ์ํธํ (ํค ์ ๋ฌ ๋ฌธ์ ํด๊ฒฐ)์ด๋ฐ ๋ฐฉ์์ผ๋ก ์ฌ์ฉ๋๋ ๋์นญ ํค๋ฅผ **์ธ์ ํค(session key)**๋ผ๊ณ ๋ถ๋ฅธ๋ค.
ํ์ด๋ธ๋ฆฌ๋ ์ํธํ์ ์ ์ฒด ํ๋ฆ
Alice๊ฐ Bob์๊ฒ ํ์ผ์ ๋ณด๋ด๋ ์ํฉ์ ์์๋ก ๋ณด์.
1. Bob์ด ๊ณต๊ฐํค/๊ฐ์ธํค ์์ ๋ง๋ฆ
2. Bob์ด ๊ณต๊ฐํค๋ฅผ Alice์๊ฒ ์ค
3. Alice๊ฐ ๋๋คํ AES ์ธ์
ํค๋ฅผ ์์ฑ
4. Alice๊ฐ ํ์ผ์ AES ์ธ์
ํค๋ก ์ํธํ
5. Alice๊ฐ AES ์ธ์
ํค๋ฅผ Bob์ ๊ณต๊ฐํค๋ก ์ํธํ
6. Alice๊ฐ [์ํธํ๋ ํ์ผ + ์ํธํ๋ ์ธ์
ํค]๋ฅผ Bob์๊ฒ ์ ์ก
7. Bob์ด ์์ ์ ๊ฐ์ธํค๋ก AES ์ธ์
ํค๋ฅผ ๋ณตํธํ
8. Bob์ด AES ์ธ์
ํค๋ก ํ์ผ์ ๋ณตํธํ์ค๊ฐ์ ๊ณต๊ฒฉ์ Eve๊ฐ ์ ์ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ๋ก์ฑ๋ Bob์ ๊ฐ์ธํค๊ฐ ์์ผ๋ฉด AES ์ธ์ ํค๋ฅผ ๋ณตํธํํ ์ ์๋ค. AES ํค๋ฅผ ๋ชจ๋ฅด๋ฉด ํ์ผ๋ ๋ณตํธํํ ์ ์๋ค. ๊ทธ๋์ ์์ ํ๋ค.
๊ณต๊ฐํค์ ์ง์ ํ์ธ
Alice๊ฐ ๋ฐ์ ๊ณต๊ฐํค๊ฐ ์ง์ง Bob์ ๊ฒ์ธ์ง ์ด๋ป๊ฒ ์๊น? ๊ณต๊ฒฉ์ Eve๊ฐ ์์ ์ ๊ณต๊ฐํค๋ฅผ Bob์ ๊ฒ์ด๋ผ๊ณ ์์ด๋ฉด Eve๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ์ ์๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ธ์ฆ์์ CA๋ฅผ ์ฌ์ฉํ๋ค. HTTPS๊ฐ ๋ฐ๋ก ์ด ๊ตฌ์กฐ๋ค. ์์ธํ ๋ด์ฉ์ 17. ์์ ์ฑ์ ์ํ ๊ธฐ์ ์ฐธ๊ณ .
Python ๊ตฌํ
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
import os
import base64
import json
def b64encode(data: bytes) -> str:
return base64.b64encode(data).decode("utf-8")
def b64decode(data: str) -> bytes:
return base64.b64decode(data.encode("utf-8"))
# 1. Bob: RSA ๊ณต๊ฐํค/๊ฐ์ธํค ์ ์์ฑ
bob_private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
bob_public_key = bob_private_key.public_key()
# 2. Alice: ๋ณด๋ผ ๋ฐ์ดํฐ ์ค๋น
plaintext = """
์ด๊ฒ์ Bob์๊ฒ ๋ณด๋ด๋ ์ค์ํ ๋ฐ์ดํฐ์
๋๋ค.
์ค์ ๋ก๋ PDF ํ์ผ, ์ด๋ฏธ์ง, JSON, DB ๋ฐฑ์
ํ์ผ ๋ฑ์ด ๋ ์ ์์ต๋๋ค.
""".encode("utf-8")
# 3. Alice: ์์ AES ์ธ์
ํค ์์ฑ
aes_key = AESGCM.generate_key(bit_length=256)
nonce = os.urandom(12)
# 4. Alice: ์ค์ ๋ฐ์ดํฐ๋ AES-GCM์ผ๋ก ์ํธํ
aesgcm = AESGCM(aes_key)
ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data=None)
# 5. Alice: AES ํค๋ฅผ Bob์ RSA ๊ณต๊ฐํค๋ก ์ํธํ
encrypted_aes_key = bob_public_key.encrypt(
aes_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
# 6. Alice โ Bob: ์ ์กํ ํจํค์ง ๊ตฌ์ฑ
package = {
"algorithm": "RSA-OAEP-SHA256 + AES-256-GCM",
"encrypted_aes_key": b64encode(encrypted_aes_key),
"nonce": b64encode(nonce),
"ciphertext": b64encode(ciphertext),
}
serialized_package = json.dumps(package, ensure_ascii=False, indent=2)
print("Alice๊ฐ Bob์๊ฒ ๋ณด๋ด๋ ํจํค์ง:")
print(serialized_package)
# 7. Bob: ํจํค์ง ์์ ํ RSA ๊ฐ์ธํค๋ก AES ํค ๋ณตํธํ
received = json.loads(serialized_package)
decrypted_aes_key = bob_private_key.decrypt(
b64decode(received["encrypted_aes_key"]),
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
# 8. Bob: ๋ณตํธํํ AES ํค๋ก ์ค์ ๋ฐ์ดํฐ ๋ณตํธํ
bob_aesgcm = AESGCM(decrypted_aes_key)
decrypted_plaintext = bob_aesgcm.decrypt(
b64decode(received["nonce"]),
b64decode(received["ciphertext"]),
associated_data=None,
)
print("\nBob์ด ๋ณตํธํํ ์๋ฌธ:")
print(decrypted_plaintext.decode("utf-8"))์ด ์ฝ๋์์ ์ญํ ์ ์ ๋ฆฌํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
| ์ญํ | ์ฌ์ฉํ๋ ๋ฐฉ์ | ์ด์ |
|---|---|---|
| ์ค์ ๋ฐ์ดํฐ ์ํธํ | AES-GCM (๋์นญ ํค) | ๋น ๋ฅด๊ณ ๋์ฉ๋ ๊ฐ๋ฅ |
| AES ํค ์ ๋ฌ | RSA-OAEP (๋น๋์นญ ํค) | ์ฌ์ ์ ๋น๋ฐ ํค๋ฅผ ๊ณต์ ํ์ง ์์๋ ๋จ |
4. ํด์ฑ
ํด์ฑ์ ์ํธํ์ ๋ค๋ฅด๋ค. ๋จ๋ฐฉํฅ ๋ณํ์ด๊ธฐ ๋๋ฌธ์ ํด์ ๊ฐ์์ ์๋ณธ ๋ฐ์ดํฐ๋ฅผ ๋ณต์ํ ์ ์๋ค.
| ๊ตฌ๋ถ | ์ํธํ | ํด์ฑ |
|---|---|---|
| ๋ฐฉํฅ | ์๋ฐฉํฅ (์ํธํ โ ๋ณตํธํ) | ๋จ๋ฐฉํฅ (์๋ณธ โ ํด์) |
| ํค | ํ์ | ๋ถํ์ |
| ์ฉ๋ | ๋ฐ์ดํฐ ๋ณดํธ | ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ, ๋น๋ฐ๋ฒํธ ์ ์ฅ |
๋ฐ์ดํฐ ํด์ฑ (SHA-256)
ํ์ผ์ด๋ ๋ฉ์์ง์ ๋ฌด๊ฒฐ์ฑ์ ๊ฒ์ฆํ ๋ ์ฌ์ฉํ๋ค.
from cryptography.hazmat.primitives import hashes
digest = hashes.Hash(hashes.SHA256())
digest.update(b"hello ")
digest.update(b"world")
result = digest.finalize()
print(result.hex())
# ์ ํด์ง ๊ธธ์ด์ ํด์ ๊ฐ ์ถ๋ ฅ (64์ hex)๋น๋ฐ๋ฒํธ ํด์ฑ๊ณผ ๋ฐ์ดํฐ ํด์ฑ์ ๋ค๋ฅด๋ค
SHA-256์ผ๋ก ๋น๋ฐ๋ฒํธ๋ฅผ ์ ์ฅํ๋ฉด ์ ๋๋ค
SHA-256์ ์๋๊ฐ ๋น ๋ฅด๊ธฐ ๋๋ฌธ์ ๋ฌด์ฐจ๋ณ ๋์ ๊ณต๊ฒฉ(brute-force)์ ์ทจ์ฝํ๋ค. ๋น๋ฐ๋ฒํธ ํด์ฑ์๋ ์๋์ ์ผ๋ก ๋๋ฆฐ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํด์ผ ํ๋ค.
๋น๋ฐ๋ฒํธ ํด์ฑ์๋ bcrypt๋ฅผ ์ฌ์ฉํ๋ค.
pip install bcryptimport bcrypt
password = b"my_secure_password"
# ํด์ฑ (salt ์๋ ์์ฑ)
hashed = bcrypt.hashpw(password, bcrypt.gensalt())
print(hashed)
# b'$2b$12$...'
# ๊ฒ์ฆ
if bcrypt.checkpw(password, hashed):
print("๋น๋ฐ๋ฒํธ ์ผ์น")
else:
print("๋น๋ฐ๋ฒํธ ๋ถ์ผ์น")| ์๊ณ ๋ฆฌ์ฆ | ์ฉ๋ | ์๋ |
|---|---|---|
| SHA-256 | ํ์ผ ๋ฌด๊ฒฐ์ฑ, ๋ฐ์ดํฐ ์ง๋ฌธ | ๋น ๋ฆ |
| bcrypt | ๋น๋ฐ๋ฒํธ ์ ์ฅ | ์๋์ ์ผ๋ก ๋๋ฆผ |
| scrypt | ๋น๋ฐ๋ฒํธ ์ ์ฅ (๋ฉ๋ชจ๋ฆฌ ์ง์ฝ์ ) | ๋๋ฆผ |
| argon2 | ๋น๋ฐ๋ฒํธ ์ ์ฅ (์ต์ ๊ถ์ฅ) | ๋๋ฆผ |
5. ์ค์ ์ ํ ๊ฐ์ด๋
์ํฉ์ ๋ฐ๋ผ ์ด๋ค ์ํธํ ๋ฐฉ์์ ์ ํํด์ผ ํ๋์ง ์ ๋ฆฌํ๋ค.
| ์ํฉ | ์ถ์ฒ ๋ฐฉ์ | ์ด์ |
|---|---|---|
| API ํค/ํ ํฐ ์ ์ฅ | Fernet | ๊ฐ๋จํ๊ณ ์ถฉ๋ถํ ์์ |
| ์ค์ ํ์ผ ์ํธํ | Fernet | ๋จ์ผ ํค๋ก ๊ฐํธํ๊ฒ ๊ด๋ฆฌ |
| ๋์ฉ๋ ํ์ผ ์ํธํ | AES-GCM | ์คํธ๋ฆฌ๋ฐ ์ฒ๋ฆฌ, ์ฑ๋ฅ ์ฐ์ |
| ์๋ฒ ๊ฐ ๋ฐ์ดํฐ ์ ์ก | AES-GCM + RSA (ํ์ด๋ธ๋ฆฌ๋) | ํค ๊ตํ ๋ฌธ์ ํด๊ฒฐ |
| ๋น๋ฐ๋ฒํธ ์ ์ฅ | bcrypt / argon2 | ๋ณตํธํ๊ฐ ํ์ ์๊ณ ๋๋ ค์ผ ์์ |
| ํ์ผ ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฆ | SHA-256 | ๋น ๋ฅด๊ณ ์ถฉ๋ ์ ํญ์ฑ ๋์ |
| ๋ฐ์ดํฐ ์๋ช /์ธ์ฆ | RSA ์๋ช | ๋ณ์กฐ ๋ฐฉ์ง + ๋ฐ์ ์ ์ฆ๋ช |
ํ๋จ ๊ธฐ์ค
- ๋ณตํธํ๊ฐ ํ์ํ๊ฐ? โ ํ์ ์์ผ๋ฉด ํด์ฑ
- ํค๋ฅผ ์๋๋ฐฉ์๊ฒ ์ ๋ฌํด์ผ ํ๋๊ฐ? โ ์ ๋ฌํด์ผ ํ๋ฉด RSA ๋๋ ํ์ด๋ธ๋ฆฌ๋
- ๋ฐ์ดํฐ ํฌ๊ธฐ๊ฐ ํฐ๊ฐ? โ ํฌ๋ฉด AES-GCM
- ๊ฐ๋จํ๊ฒ ๋๋ด๊ณ ์ถ์๊ฐ? โ Fernet
6. ํํ ์ค์์ ๋ณด์ ์ฃผ์์ฌํญ
ํค๋ฅผ ์ฝ๋์ ํ๋์ฝ๋ฉ
# ์ ๋ ํ๋ฉด ์ ๋๋ ํจํด
key = b"ZmDfcTF7_60GrrY4vBGJSVgmYR0yGH8rrOamiLkI6mA="ํค๊ฐ git ํ์คํ ๋ฆฌ์ ๋จ์ผ๋ฉด ์ญ์ ํด๋ ์ด๋ฏธ ์ ์ถ๋ ๊ฒ์ด๋ค. ํ๊ฒฝ๋ณ์๋ ์ํฌ๋ฆฟ ๋งค๋์ ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
ECB ๋ชจ๋ ์ฌ์ฉ
ECB(Electronic Codebook)๋ ๊ฐ์ ํ๋ฌธ ๋ธ๋ก์ด ํญ์ ๊ฐ์ ์ํธ๋ฌธ์ ์์ฑํ๋ค. ํจํด์ด ๊ทธ๋๋ก ๋๋ฌ๋๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ๋ฉด ์ ๋๋ค. GCM์ด๋ CBC๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
nonce/IV ์ฌ์ฌ์ฉ
AES-GCM์์ ๊ฐ์ ํค๋ก ๊ฐ์ nonce๋ฅผ ๋ ๋ฒ ์ฌ์ฉํ๋ฉด ์ํธํ๊ฐ ์์ ํ ๊นจ์ง๋ค. ๋งค๋ฒ os.urandom()์ผ๋ก ์์ฑํด์ผ ํ๋ค.
์์ฒด ์ํธํ ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ
๊ฒ์ฆ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. ์ง์ ๋ง๋ ์ํธํ๋ ๊ฑฐ์ ํ์คํ๊ฒ ์ทจ์ฝ์ ์ด ์๋ค.
SHA-256์ผ๋ก ๋น๋ฐ๋ฒํธ ํด์ฑ
์์์ ๋ค๋ค๋ฏ์ด SHA-256์ ๋น ๋ฅด๊ธฐ ๋๋ฌธ์ ๋น๋ฐ๋ฒํธ ํด์ฑ์ ๋ถ์ ํฉํ๋ค. bcrypt, scrypt, argon2๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
ํต์ฌ ์์น
- ์ง์ ๋ง๋ค์ง ๋ง๊ณ ๊ฒ์ฆ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ค
- ํค๋ ์ฝ๋์ ๋ถ๋ฆฌํ๋ค
- nonce/IV๋ ๋งค๋ฒ ์๋ก ์์ฑํ๋ค
- ๋น๋ฐ๋ฒํธ๋ ํด์ฑ, ๋ฐ์ดํฐ๋ ์ํธํ๋ก ๊ตฌ๋ถํ๋ค