기초 암호학

2025. 12. 29.
카테고리
게시일
Dec 29, 2025
이 페이지에서는 수학적 증명은 제외하고, 소프트웨어를 전공자라면 필수로 알아야 할 암호의 운용과 구조를 다룹니다.
 
 

해시 (Hash)

특정 데이터를 크기에 상관없이 고정된 크기의 정수로 변환하는 함수를 해시함수라고 함. 해시 함수는 종류도 굉장히 여러가지가 있고, 사용처도 굉장히 다양한데, 그 중 보안용으로 쓸 때는 아래 조건을 만족하는 보안 해시 함수를 사용해야 한다.
  1. 어떤 해시값만 보고, 그 해시값을 생성해내는 원본 메시지를 추측하지 못해야 함 (원상 저항성)
  1. 어떤 해시값만 보고, 그 해시값을 생성해내는 또 다른 메시지를 추측하지 못해야 함 (제2원상 저항성)
  1. 임의의 두 메시지가 같은 해시를 만들어 내지 못하도록 해야 함 (충돌 저항성)
 
현 세대에 쓰이는 대표적인 보안 해시 함수는 SHA-2 계열이다. (SHA-256, SHA-512 등)이다.
 
보안해시는 원본 메시지에서 단 한개의 비트만 달라져도 완전히 다른 해시를 생성해낸다. 해시 함수를 설계할 때의 특성상 1번은 거의 절대적으로 만족하고, 2번도 비교적 쉽게 만족하지만, SHA-1이나 MD5 같은 구형 해싱 함수들은 3번, 즉 충돌 저항성을 깨뜨리는 방법이 발견되어 현재는 사용이 권장되지 않고있다. (예를 들면 두 개의 다른 PDF 파일이 같은 SHA-1 해시를 같도록 만들 수 있음)
 
해시 함수는 컴퓨터 과학 분야에서 정말 여기저기 많이 쓰이고 있다.
  1. 비밀번호 암호화 (후술)
  1. 파일 무결성 검증 (예를 들어 파일 A의 해시값이 asdf라고 알려져 있는데, 내가 다운받은 파일 A의 해시값이 ffsa라면 이 파일은 변조된 파일이다.)
  1. 해시맵 (파일의 정보를 압축한다는 점에서 O(1)로 저장할 수 있어서 널리 쓰인다. 자세한건 HashMap 검색. 해싱 기법이 보안에만 쓰이는 것이 아니라는 것을 방증한다. 단, 해시맵에 쓰이는 해싱함수는 굳이 보안해시를 쓸 필요가 없고, 실행속도가 빠른 해시를 써도 무방하다.)
  1. 전자서명 (단, 해시만으로 전자서명을 할 수 있는 것은 아니다. 전자서명을 하는 과정에서 해시가 사용된다. 전자서명 란에서 후술)

레인보우 테이블

이미 널리 알려진 값들(password, 1234 같은 것들)은 해시값의 원본값이 널리 알려져있다. 이걸 모아둔 게 레인보우 테이블. 레인보우 테이블을 이용하면 해시값을 보고 원본값을 유추할 수 있다.

비밀번호 해싱과 Salt

비밀번호를 평문으로 저장하면, 나중에 서버 DB가 털리면 비밀번호가 고스란히 노출된다. 당연히 원래 사이트에도 공격자가 로그인이 가능해지고, 비밀번호를 공유하는 다른 사이트에도 영향을 미친다.
따라서 서비스는 유저의 비밀번호를 평문으로 저장하면 안된다.
서버는 사용자가 비밀번호를 저장할 때, 그냥 평문으로 저장하지 않고 해싱해서 저장하고, 나중에 사용자가 비밀번호를 인증할 때도 사용자가 입력한 비밀번호의 해싱값과 저장된 해시값 끼리 비교한다. 설마 비밀번호 저장 파일이 털리더라도 공격자가 원래 비밀번호를 알지 못하기 때문에 로그인이 불가능해진다. (짧은 비밀번호가 아니라면)
그래서 서버들은 원본 비밀번호를 알지 못한다.
다만 비밀번호가 레인보우 테이블에 등록되어있다면, 해시값만 보고 원본 비밀번호를 추측할 수 있게 된다. 따라서 Salt라는 것을 도입한다.
Salt는 기본적으로 해싱을 할 때 끼워넣는 랜덤한 값이다. 그리고 Salt와 해시값을 함께 DB에 저장한다.
 
비밀번호 저장 : Hash(password + Salt) ⇒ 해시값과 Salt를 DB에 저장
비밀번호 검증 : Hash(입력된 Password + Salt) ⇒ 해시값과 DB에 저장된 해시값 비교
 
설마 레인보우 테이블에 털린 비밀번호라고 해도, Salt를 함께 해싱하면 완전 다른 해시값이 나오기 때문에 해시값만 보고 원본 비밀번호를 유추할 수 없게된다.
따라서 모든 웹서비스는 Salt까지 포함해서 비밀번호를 해싱하여 저장 + 적당히 긴 비밀번호를 사용하도록 해야한다.
국내법에도 국내의 웹서비스에서 비밀번호를 평문저장하는 것을 금지하고 있다만…. 아직까지도 지켜지지 않아 종종 보안사고가 일어나고 있다.
비밀번호 해싱은 SHA-256으로 하면 너무 빠르게 브루트포스가 가능하다는 약점이 있다. (비밀번호는 메시지가 짧기 때문에 PDF 파일같은 큰 파일에 비해 해시 브루투포스가 쉽다.) 그렇기 때문에 실질적으로는 bcrypt와 같은 비밀번호 해싱에 특화된, 달리 말하면 느린 해시함수를 주로 사용하고 있다.
 

스트림 암호 vs 블럭 암호

  • 스트림 암호는 비밀키로 생성한 ‘키스트림’을 평문과 XOR해서 한 비트씩 암호화하는 방식이다.
  • 블럭 암호는 데이터를 블럭 단위로 쪼개서 암호화한다. 블럭 암호화를 마치 스트림 암호처럼 운용할 수 있다. (AES-CTR 참조)

블럭 암호 - 대칭키

하나의 비밀키를 공유하고 있는 사람끼리 메세지를 암호화, 복호화 할 수 있음. 이렇게 암호화 된 데이터를 중간에 가로채더라도 비밀키를 모른다면 내용을 알 수 없다. 속도가 후술할 비대칭키에 비해서 빠르지만, 문제는 양쪽이 비밀키를 어떻게 공유할 것인지라는 것이다. 네트워크상에서 비밀키를 공유하면 위험이 굉장히 크다. 이 문제를 해결한 것이 후술할 디피헬만이다.

AES

대표적인 대칭키 암호화 방식이다. AES는 그저 블럭을 암호화하는 알고리즘일 뿐이고, 실제로 평문과 키를 어떻게 지지고 볶고 해서 전체 메시지를 여러 블록으로 암호화 할 것인지는 운용모드에 따라 달라진다.

AES 운용모드

가장 간단한것은 ECB 방식인데, 쉽게 말하면 그냥 원본 데이터를 무식하게 블록별로 자르고, 각 청크를 AES 암호화를 돌린 것이다. 아래 사진에서 보다싶이 패턴이 그대로 공개된다는 약점이 있어 사용되지 않는다.
notion image
현재 주로 쓰이는 운용모드는 AES-GCM이다. (TLS 1.3 표준)
운용 모드 짧막 설명
AES-CTR : 카운터 값을 AES에 넣어 키스트림을 생성한다. 마치 블럭 암호화를 이용해 스트림 암호화를 하는 듯 한 운용방식이다.
  1. 키 스트림 생성
S0 = AES_K(nonce || 0) // 0번째 스트림 S1 = AES_K(nonce || 1) S2 = AES_K(nonce || 2)
  1. 암호화 / 복호화
Ci = Pi ⊕ Si // 암호화. 평문 Plaintext i 번째 블럭을 Si와 XOR연산 Pi = Ci ⊕ Si // 복호화. 암호문 Chiper i 번째 블럭을 Si와 XOR연산
AES-GCM : AES-CTR에 GHASH 인증까지 넣은 운용모드.

블럭 암호 - 비대칭키

비대칭키는 두 키 a,b를 가지는 키다.
  • a로 암호화 한 것은 b로만 복호화가 가능하다.
  • b로 암호화 한 것은 a로만 암호화가 가능하다.
  • a로 암호화 한 것은 a로 복호화가 불가능하고, b로 암호화 한 것도 b로 복호화가 불가능하다.

공개키 - 비밀키

공개키 : 두 키 중 네트워크로 공개하는 키
비밀키 : 메모리에 가지고 있는 키
 
주로 전자서명에 사용함. 과거에는 실제 통신에 대한 암호화도 비대칭키로 했었는데(TLS 1.3 이전버전) 현재는 전자서명용으로 주로 쓰임

전자서명

A라는 사람이 평소 어떤 복호화키를 공개키로 공개해놨는데, 그 공개키로 특정 메시지가 복호화 된다면, 그건 그 메시지가 A가 암호화한 것이 인증됨. 이걸 이용한게 전자서명.
대표적으로 TLS인증서도 공인 CA라는 기관의 전자서명이 된 데이터쪼가리다.
RSA 전자서명이 대표적인데, Message에 해싱과 Salt 처리를 해준 뒤 비공개키로 암호화를 해주면 서명이 된다. 이후 Salt값과 공개키로 복호화한 게 Message 해시와 동일하면 서명이 된 것이다.
 
전자서명 개념의 수도코드
  • 서명
signature = Sign(private_key, message) // 전자서명 h = Hash(message) signature = h^d mod n
  • 서명 확인
is_valid = Verify(public_key, message, signature) h = Hash(message) h' = signature^e mod n h == h' ?

키 교환 알고리즘 (디피헬만)

양 측이 서로의 공개값만 가지고 동일한 비밀키를 만드는 알고리즘
서로의 공개값이 털리더라도, 각자 메모리에 들어있는 비밀값이 안털리면 둘이 공유중인 비밀키를 절대 알아낼 수 없다.
  1. A가 공개값, 비밀값 쌍 생성, B도 공개값, 비밀값 쌍 생성
  1. A와 B가 서로 공개값를 교환한다.
  1. 각자 자신의 비밀값과 상대의 공개값으로 키를 계산한다. 이때 A, B 양쪽에서 계산된 키는 수학적으로 동일하다.
즉 공개값만 가지고 서로 안전한 키를 공유할 수 있다.
 
디피헬만은 TLS 1.3에서 세션키(대칭키)를 생성할 때 사용된다.

데이터 무결성

MAC (Message Authentication Code)

(L2 프로토콜인 Media Access Control이랑 다른 개념이니 헷갈리지 말 것)
  • HMAC : 메시지를 해싱해서 무결성을 검사한다.
 
제목: 기초 암호학작성일: 2025. 12. 29.