본문 바로가기

보안

정보보안 12주차 - 암호

1. 암호의 기초

1) 암호화 과정

  • 암호 : 중요 정보를 다른 사람들이 해석할 수 없게 하는 방법
  • 평문 : 암호화되기 전의 메시지
  • 암호문 : 암호화되고 난 후 변경된 메시지
  • 암호화 : 평문을 암호문으로 바꾸는 과정
  • 암호화 알고리즘 : 평문을 어떤 방식으로 암호문으로 변경할지 결정
  • 암호화 키 : 허락받지 않은 외부인이 암호문을 강제적으로 해독하는 것을 막음

2) 복호화 과정

  • 복호화 : 암호문을 복호화 키를 이용하여 평문으로 바꾸는 과정
  • 암호화 방식 비교
    ㆍ 대칭 암호화 알고리즘 : 암호화 키 = 복호화 키
    ㆍ 비대칭 알고리즘 : 암호화 키 != 복호화 키

2. 암호의 역사 : 고전 암호학

1) 스키테일 암호

  • 고대 스파르타에서 군사용으로 사용한 암호화 방식
  • 전치법 알고리즘 : 특정 순서에 따라 평문 배열을 재조정, 평문 문자는 그대로 두고 배열 순서만 바꾸는 방식
  • 키 : 나무 막대의 굵기
    ㆍ 같은 굵기의 막대기에 종이를 감아야 평문이 보임
    ㆍ 다르면 글자가 맞지 않아 복호화가 어려움
  • 글자를 어떻게 섞느냐가 중요함

2) 시저 암호

  • 로마의 줄리어스 시저가 군사 목적으로 사용한 암호
  • 단일 치환 알고리즘 : 알파벳을 다른 글자로 대체, 일정한 칸 수(키)만큼 밀어서 다른 글자로 변경
  • ex) 키가 3이라면, C는 3칸 밀려 F가 됨
    ㆍ COME TO ROME → FRPH WR URPH
  • 항상 키만큼의 알파벳으로 바뀌기 때문에 전사 공격에 취약함
    ㆍ 전사 공격 : 브루트포스, 하나씩 다 해보는 공격

3) 단일 치환 암호(모노 알파베틱)

  • 시저 암호보다는 복잡함
  • 알파벳 26글자를 각각 다른 알파벳에 대응시키는 방식으로 암호화
  • 알파벳별로 대응표를 만들어서 암호화
  • 시저 암호보다 보안성은 높음
  • 빈도 분석법에 취약함

4) 다중 치환

  • 단일 치환은 항상 같은 글자로 변경됨, 이를 보완
  • 같은 글자라도 위치나 키에 따라 여러 가지 글자로 대체되어 암호화됨
  • 대표적인 예 : 비즈네르 암호
    ㆍ 키 문자에 따라 다른 줄의 치환표를 사용
  • 빈도 분석법에 강함
  • ex) aaa가 표에 따라 BDG로 암호화됨

3. 대칭 암호화 방식

1) DES(Data Encryption Standard)

  • 대칭키 암호화 방식
  • 암호화 키 = 복호화 키
  • 암호화 및 복호화에 걸리는 시간이 짧으며, 비교적 간단한 방식으로 구현 가능, 현대에 가장 많이 사용
  • 64비트 평문을 64비트 암호문으로 암호화
  • 키는 56비트이고, 8비트는 오류 검출용 패리티 비트
  • 암호화 과정
    ㆍ 전처리 단계 : 초기 치환 실행 → 16번의 라운드 실행 → 후처리 단계 : 최종 치환
    ㆍ 1라운드 : 64비트를 L(32비트), R(32비트)로 나눔
    • L은 서브키가 포함된 라운드 함수 F와의 논리합 계산(XOR 연산)
    • R은 그대로 사용
    • 다음 라운드로 넘어가기 전 L과 R의 위치 변경
    • 이 과정을 16라운드 거치며 마지막 라운드에서 합침

2) 트리플 DES

  • DES의 암호화 강도를 강화한 방식
  • DES를 3번 연속 실행(= 3DES)
  • 암호화 과정
    ㆍ 평문 → K1 암호화 → K2 복호화 → K3 암호화 → 암호문
    ㆍ 2번의 암호화와 복호화 1번을 진행함
    ㆍ 호환성 유지 목적
  • 복호화 과정
    ㆍ 암호문 → K3 복호화 → K2 암호화 → K1 복호화 → 평문
    ㆍ 암호화의 반대로 처리
  • DES보다 2배의 암호화 강도를 가진다고 하지만, 충분한 암호 강도가 아니라 AES가 권장됨

3) AES

  • 미국 NIST의 암호 공모에서 채택된 대칭키 암호 방식
  • 암호 공모 조건
    ㆍ 안전성
    ㆍ 비용
    ㆍ 구현 효율성
  • 128비트 평문을 128비트 암호문으로 암호화
  • 키 길이 : 128, 192, 256비트
  • 10번의 라운드 수행
  • 암호화 과정
    ㆍ 대치 연산 : 입력 바이트를 S-box를 통해 다른 바이트로 대체
    ㆍ 치환 연산 : 행을 왼쪽으로 shift, 같은 줄에 있던 데이터 위치를 바꿔 전체적으로 섞음
    ㆍ 혼합 연산 : 열 단위로 데이터 섞음
    ㆍ AddRoundKey 연산 : 현재 State와 라운드 키를 XOR하는 과정
  • 이 과정이 하나의 라운드임
  • 첫 번째 라운드 시작 전 AddRoundKey 연산을 한 번 수행
  • 마지막 라운드에서는 혼합 연산 수행 안 함
    ㆍ 모든 라운드가 같지 않음

4) SEED

  • 국산 128비트 블록 암호 알고리즘
  • 전자상거래, 금융 등 중요 정보 보호 목적
  • SEED-128, SEED-256

5) ARIA

  • 국산 128비트 블록 암호화 알고리즘
  • 경량 환경 및 하드웨어에서의 효율성 향상을 위해 개발

6) 그 외

  • IDEA, RC5

7) 블록 암호 모드

  • 대칭 암호화 방식은 평문을 정해진 크기의 블록 단위로 나누어 암호화
  • ex) 300비트를 128비트 블록으로 나눔
    ㆍ 1번째 블록 : 128비트
    ㆍ 2번째 블록 : 128비트
    ㆍ 3번째 블록 : 44비트
    ㆍ 같은 크기로 나누다 보니 부족한 부분이 생김 → 패딩
  • 패딩 : 블록의 남은 부분에 데이터를 채워 넣는 것
    ㆍ 44비트에 84비트를 패딩하여 128비트 블록을 만듦

(1) ECB 모드

  • 각 평문 블록을 암호화한 것이 그대로 암호문 블록이 되는 방식
  • 암호화, 복호화 모두 병렬 처리 가능
  • 계산이 단순하지만 같은 평문이 같은 암호문으로 나오기 때문에 보안적으로 취약함
  • 평문의 반복이 암호문에 반영됨
  • 중간 암호문 블록의 삭제 혹은 교체에 의해 평문 변조 가능

(2) CBC 모드

  • 암호문 블록을 체인처럼 연결하는 방식
  • 현재 평문 블록을 바로 암호화하는 것이 아닌, 이전 암호문 블록과 XOR 연산 후 암호화
  • 초기화 벡터 : 첫 번째 블록은 암호문 블록이 없기에, 최초 블록 XOR 연산에 사용되는 블록
  • 보안적으로 안전하지만, 계산이 복잡함
  • 암호화 과정
    ㆍ 현재 평문 블록 ⊕ 이전 암호문 블록 → 암호화 → 암호문 블록
  • 제약사항 : 중간에 있는 블록 복호화를 위해 반드시 처음부터 복호화해야 함
  • 암호화에서 병렬 처리가 불가능

4. 비대칭 암호화 방식

  • 필요한 이유 : 대칭 암호화 방식은 키 배포 문제가 있음, 키가 노출되면 누구나 복호화 가능

1) 디피 - 헬만 키 교환

  • 비밀키를 직접 보내는 것이 아닌, 통신을 통해 비밀키를 공유하는 방법
  • 소수를 이용한 나머지 연산을 사용
  • 계산식 : 공개된 숫자 p와 g 설정, 개인키는 a, b라고 가정
  • ㆍ A = g^a mod p, B = g^b mod p 로 계산한 A, B를 서로에게 전달
  • ㆍ a의 비밀키 S = B^a mod p, B의 비밀키 S = A^b mod p

=> 비밀키를 직접 보내지 않아도 같은 비밀키를 구할 수 있음

2) RSA

  • 비대칭 암호화 방식
  • DH 키 교환에서 사용된 비대칭 알고리즘에 기반을 둔 암호화 방식
  • 공개키 : 외부에 공개할 수 있는 키(개인키를 통해 만듦)
  • 개인키 : 자신만 알아야하는 키

(1) 비대칭 암호화 방식 1

  • 공개키로 암호화, 개인키로 복호화
  • 암호화된 메시지는 개인키로만 복호화 가능 → 기밀성 보장
  • 다른 사람이 중간에 도청 불가능
  • ex) 엘리스가 밥의 공개키로 암호화 → 밥이 자신의 개인키로 복호화

(2) 비대칭 암호화 방식 2

  • 개인키로 암호화, 공개키로 복호화
  • 부인 방지 보장 : 공개키로 복호화가 가능하다는 것은 개인키로 만들어졌다는 증거가 되어, 보낸 사실을 부정하지 못하게 함
  • 전자상거래나 계약에서 중요한 법적 증거 역할

(3) 암호화와 복호화 방법

  • 평문 m을 암호화하여 c를 계산, 공개키 (E,N) : m^E mod N
  • 암호문 c를 복호화하여 m 계산, 개인키 (D,N) : c^D mod N

3) 그 외 비대칭 암호화 방식

  • 타원 곡선 암호(ECC) : RSA에 비해 작은 크기로 높은 보안성 제공, 키 크기가 작아 빠름, 자원 효율성 높아 작은하드웨어(휴대전화, 스마트카드)에서도 동작
  • 엘가말, 라빈

4) 암호화 방식 비교 : 대칭 vs 비대칭

  • 안전한 키 길이 : 128비트 이상 || 2048비트 이상
  • 키의 구성 : 비밀키 || 공개키,개인키
  • 제공 서비스 : 기밀성 || 기밀성, 부인방지 및 인증
  • 목적 : 데이터 암호화 || 키 교환
  • 단점 : 별도의 키 교환 필요 || 공개키 중간자 공격에 취약
  • 암호화 속도 : 빠름 || 느림

5. 전자서명

  • 서명 : 누군가 문서를 기록했다는 증거 → 인증 및 부인방지 역할

1) 전자서명

  • 원본 메시지에 대한 해시 값을 서명자의 개인키로 암호화하는 것
  • 개인키로 암호화, 공개키로 복호화
  • 메시지에 대한 인증, 부인방지 및 메시지 무결성 검증

2) 과정

  • 원본 메시지에 해시 함수 적용 → 해시값 생성 → 송신자의 개인키로 해시값 서명 → 전자서명 생성
  • 수신자는 받은 메시지에 해시 함수 적용, 전자서명을 송신자의 공개키로 검증하여 해시값끼리 비교

=> 해시값끼리 같으면 서명 인증이 가능함.

  • 해시 함수 : 정보의 무결성을 확인하기 위한 목적으로 사용, 입력 메시지를 역으로 확인하는 것은 불가능함

3) 전자서명의 약점

  • 송신자의 공개키가 맞는지 증명이 필요함

6. 공개키 기반 구조

1) 공개키 인증서(PKC)

  • 사용자의 공개키와 신원정보를 묶어 인증기관이 보증한 전자문서

2) CA

  • 공개키가 진짜 누구의 것인지 확인해주는 신뢰기관

=> 신뢰할 수 있는 인증기관(CA)을 이용하여 신뢰할 수 있는 안전한 공개키를 제공

3) PKI(공개키 기반 구조)

  • 공개키를 효과적으로 사용하여 안전한 암호화와 전자서명 기능등을 제공하는 보안 환경
  • ex) 인터넷뱅킹의 공인인증서

4) 구성요소

(1) 인증기관(CA)

  • 인증정책 수립, 인증서 관리, 인증서 폐기 목록 등록/관리/인증, 공개키가 진짜인지 보증

(2) 등록기관(RA)

  • 사용자 신원 확인, 인증서 요구 승인, CA에 인증서 발급 요청, 인터페이스 제공

(3) 검증기관(VA)

  • 인증서 유효성 검증/확인 주체

(4) 인증서 폐기 목록(CRL)

  • 폐기된 인증서 목록, 유출되거나 신뢰할 수 없는 인증서의 목록

(5) 디렉터리 서비스

  • 인증서, 암호키에 대한 저장/검색/관리

(6) 인증서(X.509)

  • CA가 발행한 공개키 인증서 표준 형식

인증서 발급과 검증 흐름

  • 사용자 A → RA에 인증서 요청 → RA가 CA에 인증서 발급 요청 → CA가 인증서 발급 및 배포
  • 사용자 A와 B의 거래 → 사용자 B가 디렉터리 서비스에서 인증서 확인

5) 인증서 사용방법

(1) 공개키 인증서

  • 사용자의 공개키에 사용자의 식별 정보를 추가하여 만든 일종의 전자 신분증

Step 1 : 인증서 등록 및 배포 과정

  • 밥이 개인키와 공개키 생성
  • 밥이 인증기관에 자신의 공개키 등록
  • 인증기관이 밥의 공개키와 정보 확인 후 인증서 작성
  • 인증 기관이 인증서에 디지털 서명

=> 밥의 인증서에는 밥의 공개키 + 인증기관의 디지털 서명 정보가 들어감

Step 2 : 인증서 검증 및 공개키 사용

  • 앨리스가 밥의 인증서를 받음
  • 앨리스는 인증기관의 공개키로 인증서 서명을 검증
  • 인증서가 정상이면 밥의 공개키를 신뢰
  • 앨리스는 밥의 공개키로 메시지 암호화
  • 밥은 자신의 개인키로 메시지 복호화

=> 밥의 공개키를 그냥 믿는 것이 아닌 인증기관의 서명을 인증기관의 공개키로 검증 후 신뢰함

(인증기관의 인증서는 인증 기관의 공개키와 상위 인증 기관의 서명 으로 이루어짐)

(2) 확인 과정의 상세 내용

  • 인증서에 포함될 밥의 공개키와 신원 정보 등을 해시하여 Message Digest를 생성함
  • 이를 CA의 개인키로 서명함
  • 결국 밥의 공개키 인증서는 밥의 공개키, 신원 정보, CA의 전자서명으로 이루어짐
  • 클라이언트는 인증서 안의 정보에 해시 함수를 적용하여 Message Digest를 생성함
  • CA의 디지털 서명을 CA의 공개키로 검증하여 또 다른 Message Digest를 추출함
  • 두 Message Digest를 비교하여 같으면 인증서가 변조되지 않았고, CA가 보증한 인증서임을 확인할 수 있음

=> 즉 사용자는 인증서를 받은 뒤 CA의 공개키로 서명을 검증하여 인증서가 진짜인지 확인함.

인증서 연결 구조

  • 여기서의 의문점 : CA의 공개키는 믿을만 한가?

=> 인증서 연결 구조 등장(신뢰 체인)

  • 검증 방법 : 인증서의 발급 기관 이름을 추적하여 상위 기관의 인증서를 찾음
  • 그 후 발급자 서명과 상위 기관 인증서의 공개키를 이용해 인증서의 서명을 검증함
  • 이 과정이 중간 CA를 거쳐 최상위 Root CA까지 이어짐
  • 최상위 기관 : Root CA 기관 이름(Comodo, Godaddy 등), 자체 서명, Root CA 공개키, 인증서 해시값을 가짐ㆍ 서버 인증서는 이 루트 인증서에서 이어지는 체인에 포함되어야 신뢰됨
  • ㆍ 브라우저나 운영체제가 미리 신뢰하고 있음
  • 중간 기관 : 중간 CA 이름, 발급 기관 이름(Root CA), 공개키, 인증서 해시값, 발급자 서명을 가짐ㆍ 서버 인증서는 중간 CA의 개인키로 서명되어 있음
  • ㆍ 중간 CA 인증서는 Root CA의 개인키로 서명되어 있음

=> 이처럼 인증서가 상위 인증기관의 서명으로 계속 연결되어 최종적으로 Root CA까지 이어지는 구조를 신뢰 체인이라고 함

6) 상호인증

  • 두 인증기관이 상대방의 공개키를 서로 인증해주는 인증서를 발급하여 사용하는 것
  • 상호 인증서

=> 서로 다른 인증기관끼리 상대방의 공개키를 신뢰할 수 있도록 인증서를 발급


5. 암호화 코드 실습

1) 대칭키

  • 암호화 키 = 복호화 키
  • 속도가 빠름
  • but, 키 교환 문제, 송신가와 수신자가 같은 키를 가져야해서 안전하게 전달해야함

=> 전달 중 탈취되거나 노출될 위험 있음, 통신 상대 많아지면 키 관리가 복잡해짐

(1) cryptopgraphy 라이브러리의 Fernet 사용

pip install cryptography
from cryptography.fernet import Fernet

# 키 생성
key = Fernet.generate_key() #  대칭키 값 자체를 생성 ex ) key = b'abc123...'
print(f"[key] {key}") # 대칭키 값을 출력
cipher_suite = Fernet(key) # 암호화/복호화 기능을 가진 객체를 생성

# 암호화
text = b"Hello, cryptography!"  # 평문, 바이트 데이터로 처리해야함
cipher_text = cipher_suite.encrypt(text)  #암호화
print(f"Cipher Text: {cipher_text}")

# 복호화
decrypted_text = cipher_suite.decrypt(cipher_text)  #복호화
print(f"Decrypted Text: {decrypted_text}") # 복호화된 평문 출력

(2) pycryptodome : AES

pip install pycryptodome
from Crypto.Cipher import AES  # AES 암호화 기능 import
from Crypto.Random import get_random_bytes  # 랜덤 바이트 생성 기능

# 암호화 키 생성
key = get_random_bytes(16)  # AES 대칭키 값 자체를 16바이트로 생성 (16바이트 = 128비트 : AES-128 키)

# 암호화 객체 생성
cipher = AES.new(key, AES.MODE_EAX) # 암호화 객체 생성 (암호화와 무결성 검증을 같이 제공)
# 내부적으로 cipher.nonce가 자동 생성 (비밀값이 아닌 이번 암호문 작업에서 쓰이는 일회성 값으로 같이 전송함)

# 암호화할 데이터
data = b"Secret Message"

# 데이터 암호화
ciphertext, tag = cipher.encrypt_and_digest(data) # =암호화 결과로써 암호문과 인증태그 생성 (변조 여부 확인)

# 복호화 객체 생성
cipher_dec = AES.new(key, AES.MODE_EAX, cipher.nonce) # 암호화할때 사용한 nonce도 사용해야함

# 데이터 복호화
decrypted_data = cipher_dec.decrypt_and_verify(ciphertext, tag) #암호문을 복호화, tag로 변조여부 확인

print("Original:", data)
print("Decrypted:", decrypted_data)

2) 비대칭키

  • 암호화 키 ≠ 복호화 키
  • 공개키와 개인키를 각각 가지고 있고, 공개키는 공개키 저장소를 통해 모든 사람이 접근 가
  • 키 분배와 관리가 편하지만, 키 길이가 길고 연산이 복잡해서 속도가 느림
  • 기밀성, 무결성, 부인 방지 제공
pip install pynacl  #PyNaCl 라이브러리  사용
from nacl.public import PrivateKey, Box

# Box는 송신자의 개인키와 수신자의 공개키로 암호화, 송신자의 공개키와 수신자의 개인키로 복호화

# 송신자의 공개키/비밀키 쌍 생성
sk = PrivateKey.generate()
pk = sk.public_key

# 수신자의 공개키/비밀키 쌍 생성
receiver_sk = PrivateKey.generate()
receiver_pk = receiver_sk.public_key

# 송신자의 Box 생성
sender_box = Box(sk, receiver_pk) # 송신자의 개인키와, 수신자의 공개키로 암호화할 Box를 만듦

# 메시지 암호화
message = b"Hello PyNaCl"
encrypted = sender_box.encrypt(message)

# 수신자의 Box 생성
receiver_box = Box(receiver_sk, pk) # 수신자의 개인키와, 송신자의 공개키로 복호화할 Box 만듦

# 메시지 복호화
decrypted = receiver_box.decrypt(encrypted)

print("Encrypted message:", encrypted)
print("Decrypted message:", decrypted)