정보보안의 원리
웹 416쪽 무료 · 종이책 B5 416쪽
© K-BOOKS · 무료 웹 열람용 콘텐츠입니다. 무단 복제·저장·인쇄·재배포를 제한합니다.
기본 블록 암호는 한 덩어리의 입력을 바꾸는 함수입니다. 실제 파일은 여러 덩어리이고 길이도 제각각입니다. 운용 모드는 이 함수를 긴 메시지에 적용하는 약속입니다. 가장 단순한 ECB는 블록마다 독립적으로 같은 키를 적용합니다. 동일한 평문 블록이 동일한 암호문 블록이 되므로 반복 구조가 남습니다. 그림이나 서식이 반복되는 데이터의 기밀성을 보호하는 일반적인 모드로 선택하지 않습니다.
CBC는 현재 평문 블록을 이전 암호문 블록과 XOR한 뒤 암호화합니다. 첫 블록에는 이전 암호문이 없으므로 초기화 벡터, IV를 넣습니다. 암호문 앞부분이 뒤 블록의 계산에 영향을 주어 ECB의 반복 문제를 줄입니다. 메시지 길이가 블록 크기의 배수가 아니면 약속된 패딩으로 채웁니다. CBC 암호화에는 예측하기 어려운 IV가 필요하며, IV는 키처럼 비밀로 숨겨야 하는 값은 아닙니다. 그렇다고 공격자가 마음대로 바꾸어도 된다는 뜻은 아닙니다. IV와 암호문을 인증하지 않으면 복호화되는 내용에 영향을 줄 수 있습니다.
CFB는 앞선 암호문 상태를 암호화해 얻은 값을 평문과 결합합니다. OFB는 암호 함수의 출력을 다시 입력으로 넣어 키스트림을 이어갑니다. CTR은 nonce와 증가하는 카운터로 서로 다른 입력 블록을 만들고, 그 암호화 결과를 평문과 XOR합니다. CTR은 블록별 계산을 병렬로 수행하기 쉽고 부분 데이터에도 키스트림을 잘라 적용할 수 있습니다. 하지만 같은 키에서 카운터 입력을 반복하면 같은 키스트림이 나옵니다. OFB도 같은 키와 IV로 같은 흐름을 다시 만들면 안 됩니다. “IV는 아무 난수나 넣는다”는 한 문장으로 모든 모드의 조건을 설명할 수 없는 이유입니다.
nonce는 특정 범위에서 한 번만 쓰도록 하는 값입니다. 대개 공개되어도 되지만 같은 키 아래에서 반복되면 안 되는 경우가 많습니다. 카운터를 nonce로 쓸 수 있는 방식도 있지만, 서버 재시작이나 여러 장비가 한 키를 공유할 때 중복되지 않도록 상태를 관리해야 합니다. CBC의 IV에는 예측 불가능성이 요구되는 반면, CTR과 GCM에서는 같은 키에 대한 입력·nonce의 유일성이 핵심입니다. 라이브러리가 요구하는 길이와 생성 규칙을 따르고, 키를 재사용하는 범위 전체에서 중복을 막습니다.
한 바이트 XOR만으로 재사용의 구조를 봅니다. 이 예는 실제 암호가 아닌 계산 설명입니다.
평문 A를 0x41, 평문 B를 0x42, 같은 키스트림 바이트를 0xAA라고 해 보겠습니다. 암호문은 각각 0xEB와 0xE8입니다.
두 암호문을 XOR하면 0xEB XOR 0xE8 = 0x03입니다. 같은 키스트림은 두 번 등장하면서 사라지고 0x41 XOR 0x42만 남습니다.
공격자가 첫 평문 0x41을 알면 0x03 XOR 0x41 = 0x42로 두 번째 평문을 얻습니다. 현실에서는 파일의 고정 머리말처럼 일부 평문을 아는 상황이 흔합니다. nonce 중복은 멋대로 외우는 금지 규칙이 아니라 이러한 정보 노출을 막기 위한 조건입니다.
깨달음XOR 자체가 나빠서가 아니라 같은 비밀 흐름을 두 메시지에 사용한 것이 문제입니다.
앞의 기밀성 모드들은 암호문이 바뀌었는지를 스스로 보증하지 않습니다. 현대적인 프로토콜은 기밀성과 무결성을 함께 제공하는 인증 암호를 널리 사용합니다. GCM은 CTR 방식의 암호화와 인증 태그 계산을 결합한 모드입니다. AEAD라는 인터페이스에서는 평문 외에 부가 인증 데이터, AAD도 넣을 수 있습니다. AAD는 숨기지는 않지만 변경되면 검증이 실패하도록 묶는 정보입니다. 예를 들어 파일의 버전이나 레코드 종류는 밖에서 읽어야 해도 암호문과 관계가 바뀌면 안 됩니다.
# 격리한 Python 환경에 cryptography 패키지가 필요하다.
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.exceptions import InvalidTag
key = AESGCM.generate_key(bit_length=128)
aead = AESGCM(key)
nonce = os.urandom(12)
aad = b'homework:v1'
sealed = aead.encrypt(nonce, b'score=80', aad)
print(aead.decrypt(nonce, sealed, aad).decode())
changed = bytearray(sealed)
changed[0] ^= 1
try:
aead.decrypt(nonce, bytes(changed), aad)
except InvalidTag:
print('변조된 데이터 거부')출력은 score=80 다음 변조된 데이터 거부입니다. 예제는 한 번 암호화한 결과의 복호화와 변조 검사를 합니다. 새 메시지를 암호화할 때는 같은 키에 nonce를 재사용하지 않습니다.
여기서 sealed는 암호문과 인증 태그를 담습니다. 복호화는 올바른 키뿐 아니라 같은 nonce와 AAD를 받아야 성공합니다. 한 비트 변경 뒤에는 내용이 조금 이상해진 평문을 사용하지 않고 오류로 거부합니다. 실제 서비스도 검증 완료 전의 평문을 외부로 내보내거나 업무 처리에 쓰지 않아야 합니다. 태그가 실패했다는 사실을 기록하되 비밀 키나 평문 전체를 오류 로그에 남기지 않습니다. ChaCha20-Poly1305도 같은 AEAD 목적을 제공하지만 내부 알고리즘은 AES-GCM과 다릅니다.
무료 학습은 그대로, 종이책은 필요한 분을 위한 선택지입니다.
웹 416쪽 무료 · 종이책 B5 416쪽
웹 513쪽 무료 · 종이책 B5 513쪽