암호학 실무에서 자주 무너지는 지점은 알고리즘 자체보다 난수, 키 관리, 운영 모드, 오류 처리, 인증 검증을 조합하는 설계다. 이 글은 보안 진단자와 리버서 관점에서 암호 구현을 볼 때 무엇을 의심하고 확인해야 하는지 정리한다.
대상: 보안 운영자, 진단자, 리버서
관점: 구현 실수와 설계 실패
버전: 복원/확장 병합본 2026
관련 영상
문서 흐름
- 암호학은 수학보다 설계다
- 난수와 엔트로피
- 무결성, 인증, 비밀번호 저장
- 키 관리와 KDF
- CBC / CTR / GCM 운영 모드
- RSA, PKCS#1 v1.5, OAEP
- 암호학적 리버싱
- 취약점 분석 패턴
- 양자 내성 전환
- 감사 체크리스트
0. 들어가며: 암호학은 왜 “수학”이 아니라 “설계”인가?
많은 보안 전문가가 암호학을 복잡한 수학 공식의 집합으로 생각하고 멀리합니다. 하지만 실무 보안에서 더 자주 무너지는 지점은 알고리즘의 수학적 안전성이 아니라 알고리즘을 조립하고 운영하는 방식입니다.
강한 알고리즘을 썼다는 말은 출발점일 뿐입니다. 실무 질문은 “무슨 알고리즘인가?”가 아니라 “어떤 키로, 어떤 모드로, 어떤 난수로, 어떤 오류 처리와 인증 검증을 붙여 사용했는가?”입니다.
따라서 이 문서는 수학 증명보다는 보안 진단자가 실제 코드, 바이너리, 프로토콜, 운영 환경을 볼 때 무엇을 의심해야 하는지에 초점을 둡니다.
1. 난수와 엔트로피: 예측 가능성의 위험성
암호화의 핵심은 예측 불가능성입니다. 키, IV, Nonce, Salt, challenge, 세션 토큰처럼 보안 경계를 구성하는 값은 공격자가 미래 값을 예측하거나 과거 값을 재구성할 수 없어야 합니다.
분석 사례: 범위가 좁은 시드
특정 모듈이 세션키를 만들 때 gettimeofday().tv_usec 값을 시드로 사용한다고 가정해봅시다. 마이크로초 값은 0부터 999,999까지라 경우의 수가 최대 1,000,000개에 불과합니다. 공격자가 요청 시각을 대략 알고 있다면 실제 탐색 공간은 훨씬 더 좁아집니다.
위험한 패턴
rand(),rand_r(),Math.random()계열로 키 생성- 시간, PID, 카운터, 사용자 ID를 시드로 사용
- 난수값을 로그나 디버그 화면에 출력
- 테스트용 고정 시드를 운영에 그대로 사용
안전한 원칙
- 운영체제 또는 검증된 라이브러리의 CSPRNG 사용
- 키와 Nonce 생성 책임을 애플리케이션 임의 로직에 두지 않기
- 분산 시스템에서는 충돌 가능성을 설계 단계에서 제거
- 장기 비밀값과 일회성 난수값의 용도를 분리
전문가의 눈: CSPRNG
암호키, IV, Nonce, Salt 등은 반드시 OS 수준 CSPRNG 또는 검증된 암호 라이브러리 API로 생성해야 합니다. 핵심은 단순히 “랜덤해 보이는 값”이 아니라 공격자가 내부 상태를 모르는 상황에서 다음 출력을 예측할 수 없는 값입니다.
2. 무결성, 진정성, 비밀번호 저장
무결성(Integrity)과 진정성(Authenticity)은 다르다
일반 해시 → 무결성
SHA-256 같은 해시는 데이터가 바뀌었는지 확인하는 지문 역할을 합니다. 하지만 누구나 다시 계산할 수 있으므로 공격자가 데이터를 바꾸고 해시도 함께 바꾸면 검증이 무력화됩니다.
MAC / AEAD 태그 → 진정성
HMAC이나 AEAD 인증 태그는 비밀키를 아는 주체만 만들 수 있습니다. 따라서 메시지가 바뀌지 않았는지뿐 아니라, 권한 있는 주체가 만든 메시지인지까지 확인할 수 있습니다.
실무 실수: “암호문 + SHA-256”
암호문 옆에 단순 SHA-256 해시를 붙이는 것은 인증이 아닙니다. 공격자도 암호문을 변조한 뒤 해시를 다시 계산할 수 있기 때문입니다. 인증을 원한다면 HMAC 또는 AEAD를 사용해야 합니다.
비밀번호는 암호화하지 말고 해시해야 한다
비밀번호는 시스템이 평문을 다시 알아야 하는 데이터가 아닙니다. 따라서 AES 같은 대칭키 암호로 “복호화 가능한 형태”로 저장하는 것은 원칙적으로 부적절합니다.
- 비밀번호 저장에는 Argon2id, bcrypt, scrypt, PBKDF2 같은 전용 Password Hashing 알고리즘을 사용합니다.
- 사용자별 Salt를 적용해 같은 비밀번호라도 다른 저장값이 나오게 해야 합니다.
- 오프라인 대입 공격을 늦추기 위해 충분한 비용 파라미터를 설정해야 합니다.
- 단순 SHA-256, MD5, Base64, “자체 암호화”는 비밀번호 저장 방식이 아닙니다.
3. 키 관리: 암호의 강도는 키 운영에서 결정된다
암호 알고리즘이 강해도 키가 잘못 생성·저장·공유·회전되면 전체 시스템은 쉽게 무너집니다. 실무 감사에서 가장 먼저 봐야 할 것은 “어떤 알고리즘을 썼는가”가 아니라 키가 어디서 와서 어디로 가는가입니다.
KDF와 Key Separation
하나의 마스터 비밀값에서 여러 용도의 키가 필요할 때 단순히 문자열을 자르거나 해시를 한 번 수행하는 방식은 위험합니다. 이런 경우에는 HKDF 같은 KDF를 사용해 용도별 키를 분리해야 합니다.
왜 HKDF를 쓰나요? 주스 착즙 비유
- Extract: 원본 비밀값, 예를 들어 키 교환 결과에 섞여 있을지도 모를 편향과 불순물을 걸러내고 암호학적으로 다루기 좋은 의사난수 키를 추출합니다.
- Expand: 목적 문자열(info/context)을 넣어 암호화용 키, MAC용 키, 세션별 키, 송신 방향 키, 수신 방향 키처럼 서로 다른 컵에 나누어 담습니다.
이렇게 하면 Key Separation이 달성됩니다. 하나의 키를 암호화와 인증, 송신과 수신, 파일 암호화와 토큰 서명에 동시에 쓰면 알고리즘 사이의 상호작용이나 키 노출 범위가 커질 수 있습니다. HKDF는 같은 뿌리에서 나온 비밀값을 서로 독립적인 용도별 키로 분리해, 한 영역의 문제가 다른 영역으로 번지는 위험을 낮춥니다.
키 저장과 운영
저장 위치
소스코드, Git 히스토리, Docker 이미지, 평문 설정 파일, 로그에 키가 남아 있지 않은지 확인합니다. 환경 변수도 /proc/self/environ, 디버그 덤프, 런타임 진단 도구를 통해 노출될 수 있으므로 운영 비밀값은 가능하면 KMS, HSM, Secret Manager로 분리합니다.
접근 권한
KMS, HSM, Secret Manager 등 전용 시스템을 사용하고 최소 권한 원칙을 적용합니다.
수명 주기
키 회전, 폐기, 사고 시 재발급, 과거 데이터 재암호화 정책이 존재하는지 확인합니다.
4. 운영 모드의 이해와 취약점: CBC / CTR / GCM
CBC 모드와 IV
CBC에서 IV는 비밀값일 필요가 없습니다. 일반적으로 암호문과 함께 전송될 수 있습니다. 중요한 것은 암호화 시점에 IV가 예측 불가능해야 하며, IV가 무결성 보호 범위에 포함되어야 한다는 점입니다.
IV가 변조되면 첫 번째 평문 블록이 공격자가 의도한 방식으로 바뀔 수 있습니다. 따라서 CBC를 쓴다면 “암호화만”으로는 부족하고 반드시 MAC 또는 AEAD 수준의 인증이 붙어야 합니다.
P_1 = D_k(C_1) XOR IV
P_n = D_k(C_n) XOR C_{n-1}
분석가 관점에서는 대칭키를 확보했지만 IV를 모르는 특수 상황에서도, CBC 구조상 첫 번째 블록을 제외한 나머지 블록은 이전 암호문 블록을 이용해 복호화할 수 있습니다.
CTR 모드와 Nonce
Nonce 재사용은 구조적 재앙
CTR/GCM 계열에서 Nonce는 비밀값이 아닙니다. 핵심은 공격자가 Nonce를 아느냐가 아니라 동일 키에서 Nonce가 재사용되었느냐입니다. 같은 키와 Nonce 조합이 반복되면 같은 키스트림이 생성되고, 두 암호문을 XOR했을 때 평문 간 XOR 관계가 드러납니다.
C1 = P1 XOR Stream(K, Nonce)
C2 = P2 XOR Stream(K, Nonce)
C1 XOR C2 = P1 XOR P2
AES-GCM과 AES-GCM-SIV
AES-GCM
현대 프로토콜에서 널리 쓰이는 고성능 AEAD입니다. 기밀성과 인증을 함께 제공하지만, Nonce 재사용에 매우 민감합니다.
AES-GCM-SIV
Nonce가 실수로 재사용되어도 일반 GCM처럼 즉시 파국으로 이어지지 않도록 설계된 misuse-resistant AEAD입니다. 다만 Nonce 재사용을 권장한다는 뜻은 아닙니다.
AEAD의 절대 원칙
태그 검증에 실패한 평문은 존재하지 않는 것으로 취급해야 합니다. 라이브러리가 내부 버퍼에 일부 평문을 생성했더라도 애플리케이션은 이를 절대 사용해서는 안 됩니다.
5. RSA 암호화와 PKCS#1 v1.5: “깨진 것”과 “위험한 것”은 다르다
RSA를 볼 때 가장 먼저 구분해야 하는 것은 암호화와 서명입니다. RSAES는 암호화, RSASSA는 서명입니다. 둘은 같은 RSA 수학 구조를 공유하지만 안전성 조건과 패딩 방식이 다릅니다.
PKCS#1 v1.5 암호화 패딩의 위치
RSAES-PKCS1-v1_5의 패딩 포맷은 대략 다음과 같습니다.
EM = 0x00 || 0x02 || PS || 0x00 || M
PS는 0이 아닌 무작위 바이트열입니다.- 이 구조 자체가 “새로운 수학적 붕괴”를 맞은 것은 아닙니다.
- 하지만 chosen-ciphertext 상황에서 복호화 오류 차이, 시간 차이, 로그 차이, 응답 차이가 보이면 padding oracle이 될 수 있습니다.
핵심 판단: PKCS#1 v1.5 자체보다 Oracle 노출 여부가 중요하다
외부 사용자가 임의 암호문을 반복 제출할 수 있고, 서버가 패딩 오류·길이 오류·키 오류·MAC 오류를 서로 다르게 보여주거나 시간 차이를 노출한다면 위험합니다. 이때 공격자는 “복호화 결과를 직접 보지 않고도” 내부 정보를 조금씩 얻을 수 있습니다.
| 상황 | 판단 | 실무 조치 |
|---|---|---|
| 신규 설계에서 RSA 암호화 필요 | PKCS#1 v1.5 선택 금지에 가깝게 판단 | RSA-OAEP 또는 KEM/하이브리드 구조 사용 |
| 기존 시스템 호환 때문에 PKCS#1 v1.5 유지 | Legacy risk | 복호화 API 노출 최소화, 오류 응답 통합, timing 차이 점검 |
| 랜덤 CEK/세션키 래핑 용도 | 상대적으로 제한적 위험 | RNG, 키 길이, oracle 방어, 실패 처리 확인 |
| 사용자 입력 메시지를 직접 RSA로 암호화 | 부적절 | 하이브리드 암호화 구조로 변경 |
RSA-OAEP와 RSA-PSS
암호화: RSA-OAEP
신규 RSA 암호화에서는 OAEP가 기본 선택입니다. OAEP도 구현과 파라미터 선택이 중요하지만, PKCS#1 v1.5 암호화보다 현대적인 보안 모델에 맞습니다.
서명: RSA-PSS
서명에서는 PKCS#1 v1.5 서명보다 RSA-PSS가 현대 권장 방식입니다. 암호화 패딩 논의와 서명 패딩 논의를 혼동하지 않아야 합니다.
PKCS#1 v1.5 복호화 감사 포인트
- 복호화 실패 원인이 외부 응답에서 구분되지 않는가?
- 성공/실패 경로의 처리 시간이 관찰 가능하게 다른가?
- 패딩 검사, 길이 검사, 구분자 검색이 데이터 의존 분기를 만드는가?
- 실패 시 랜덤 대체값 또는 implicit rejection 같은 방어가 적용되는가?
- RSA blinding이 적용되어 개인키 연산의 side-channel 위험을 줄이는가?
- 복호화 API가 외부에 직접 노출되어 있거나 반복 호출 가능한가?
결론
**“PKCS#1 v1.5 무작위 패딩 로직 자체에 새로운 대형 붕괴가 있느냐?”**라는 질문에는 보통 “그런 의미는 아니다”라고 답할 수 있습니다. 그러나 **“신규 시스템에서 안전한 선택인가?”**라는 질문에는 “아니다. OAEP 또는 KEM 기반 구조로 가야 한다”가 실무적 답입니다.
6. 분석가의 눈: 암호학적 리버싱과 프로토콜 해부
표준 문서는 알고리즘의 수학적 명세를 설명하지만, 실제 구현체의 데이터 흐름은 바이너리 분석과 동적 분석으로 확인해야 합니다. 특히 상용 모듈은 표준 알고리즘 위에 커스텀 래핑, 인코딩, 엔디안 변환, 키 스케줄 캐싱을 덧씌우는 경우가 많습니다.
Step 1. 엔진 식별
S-Box, 라운드 상수, 초기 벡터, 상수 테이블, 함수 호출 패턴을 통해 AES, SEED, ARIA, DES, RSA 등을 식별합니다.
Step 2. 모드 식별
CBC, CTR, GCM, ECB 등 운영 모드를 확인합니다. 같은 블록 암호라도 모드가 다르면 보안 성질이 완전히 달라집니다.
Step 3. 래핑 확인
패딩, 커스텀 Base64, 문자열 치환, 엔디안 처리, 헤더 삽입, MAC 결합 순서를 확인합니다.
마스터 키와 라운드 서브키
리버싱 중 GDB나 Frida로 키처럼 보이는 값이 여러 개 보인다면 Key Schedule 때문일 수 있습니다. 예를 들어 블록 암호는 하나의 마스터 키에서 라운드별 서브키를 생성합니다. 분석 목적상 전체 키 스케줄 배열을 덤프하면 복잡한 키 생성 로직을 끝까지 해석하지 않고도 암·복호화 재현이 가능한 경우가 있습니다.
바이너리 속 암호학 DNA: S-Box, 상수, 회전
암호 알고리즘을 리버싱할 때 마주치는 S-Box, 라운드 상수, 회전 연산은 단순한 구현 잡음이 아니라 알고리즘의 설계 의도를 보여주는 강한 단서입니다. 이 부분은 복구본의 설명을 살려 분석가 관점의 언어로 보강했습니다.
1. S-Box: 비밀 메뉴판과 혼돈(Confusion)
S-Box는 입력과 출력 사이에 비선형 관계를 만드는 치환 테이블입니다. 단순한 덧셈이나 XOR만 반복되는 구조라면 선형식으로 모델링될 수 있지만, S-Box는 입력을 엉뚱한 출력으로 튀게 만들어 수학적 역산 비용을 높입니다.
- 식별 단서: 256바이트 테이블, 고정 치환표, AES/SEED/ARIA 계열 상수 배열.
- 분석 의미: 선형·미분 분석을 어렵게 하는 핵심 구성 요소이며, 바이너리에서 알고리즘을 식별하는 지문 역할을 합니다.
비유하자면 선형 관계는 1→2, 2→4처럼 예측 가능한 지도이고, 비선형 관계는 1→9, 2→0처럼 규칙을 일부러 뒤틀어 하나의 공식으로 접히지 않게 만드는 장치입니다.
2. 공개 검증 가능한 상수: Nothing-up-my-sleeve
원주율, 제곱근, 특정 다항식 기반 상수는 난수원이라기보다 설계자가 임의 숫자를 골라 백도어를 숨기지 않았다는 공개 검증성을 제공합니다. “숫자를 어디서 가져왔는가”를 설명할 수 있어야 알고리즘 설계에 대한 신뢰가 생깁니다.
- 라운드 상수: 반복 구조가 자기 자신과 겹치는 것을 막고 slide attack 같은 구조적 공격을 어렵게 합니다.
- 로또 추첨 비유: 상수가 당첨 번호라면 공개 검증성은 추첨 생중계입니다. 설계자가 밀실에서 숫자를 정하지 않았다는 증거입니다.
3. 회전 및 비트 연산: 나비 효과와 확산(Diffusion)
회전(rotate), 시프트, XOR, AND, OR 같은 비트 연산은 암호 구현에서 자주 등장합니다. 이들은 CPU가 빠르게 처리할 수 있는 연산이면서, 입력의 작은 변화가 여러 라운드를 거치며 전체 출력으로 퍼지도록 돕습니다.
- 쇄도 효과: 입력 비트 하나가 바뀌었을 때 출력의 많은 비트가 바뀌어야 합니다.
- 잉크 한 방울 비유: 맑은 물에 잉크 한 방울을 떨어뜨린 뒤 휘저으면 전체 색이 바뀌듯, 회전과 XOR은 작은 차이를 전체 상태로 확산시키는 교반기 역할을 합니다.
- 리버싱 단서: 일정한 rotate-left/rotate-right 패턴, 라운드별 상수와 XOR되는 구조, 워드 단위 엔디안 변환이 반복되면 특정 계열의 블록 암호나 해시 함수를 의심할 수 있습니다.
수학적 백도어란? 조작된 자물쇠 비유
일반적인 백도어가 비밀 패스워드를 심는 것이라면, 수학적 백도어는 자물쇠 내부 핀을 미세하게 깎아두는 것에 가깝습니다. 겉보기에는 튼튼해 보이지만 설계자만 아는 특정 관계, 상수, 곡선 파라미터, 난수 생성 구조가 있으면 계산 지름길이 생길 수 있습니다.
따라서 리버싱과 설계 검토에서는 “상수가 무엇인가”뿐 아니라 그 상수가 왜 선택되었는가, 선택 과정이 공개적으로 검증 가능한가, 테스트 벡터와 표준 문서가 일관되는가를 함께 확인해야 합니다.
리버싱 식별 포인트
| 대상 | 식별 단서 | 주의점 |
|---|---|---|
| ECB | 블록 간 연결 없이 같은 입력 블록이 같은 출력 블록으로 반복 | 이미지·정형 데이터 패턴 누출 |
| CBC | 복호화 결과와 이전 암호문 블록의 XOR | IV 무결성, padding oracle 확인 |
| CTR | 증가하는 카운터를 암호화한 뒤 평문과 XOR | Nonce 재사용과 malleability 확인 |
| GCM | CTR 흐름 + GHASH/Galois Field 곱셈 루틴 | Nonce 재사용, 태그 검증 실패 처리 확인 |
| RSA | 큰 정수 모듈러 지수 연산, DER key parsing, OAEP/v1.5 depadding | 복호화 oracle, padding check timing 확인 |
테스트 벡터 검증
알고리즘을 직접 구현하거나 리버싱으로 복원했다면 반드시 표준 테스트 벡터와 비교해야 합니다. S-Box와 라운드 함수가 맞아 보여도 엔디안, 패딩, IV 적용, 인코딩 방식 하나로 결과가 완전히 달라질 수 있습니다.
7. 암호 구현 취약점 분석 패턴
암호 구현의 취약점은 대부분 “약한 알고리즘”이 아니라 경계 조건, 오류 처리, 키 관리, 재사용, 인증 누락에서 발생합니다.
| 패턴 | 위험 | 분석 질문 |
|---|---|---|
| Padding Oracle | CBC/RSA v1.5에서 복호화 정보 누출 | 오류 메시지, HTTP status, timing, 로그가 다른가? |
| Nonce/IV 재사용 | CTR/GCM에서 평문 관계 또는 인증키 정보 노출 | 분산 환경에서 충돌 방지가 설계되어 있는가? |
| Encrypt-then-Hash | 단순 해시 재계산 가능 | 비밀키 기반 MAC 또는 AEAD 태그인가? |
| 태그 검증 무시 | 변조된 평문 사용 | 검증 실패 예외를 삼키고 계속 처리하지 않는가? |
| 키/시드 로그 노출 | 사후 복호화와 세션 탈취 | 디버그 로그, APM, crash dump에 비밀값이 남는가? |
| 다운그레이드/폴백 | 강한 모드에서 약한 모드로 강제 전환 | 호환성 옵션이 공격자가 선택 가능한가? |
실무 진단 문장
“AES-256을 사용하므로 안전하다”는 결론은 불충분합니다. “AES-256-GCM을 사용하며, 키는 CSPRNG/KMS 기반으로 생성·관리되고, Nonce는 동일 키에서 재사용되지 않으며, 태그 검증 실패 시 평문을 폐기한다” 정도까지 확인해야 실무적으로 의미 있는 판단입니다.
8. 미래 위협: 하이브리드 양자 내성 프로토콜
양자 내성 전환은 “RSA를 당장 모두 버린다”는 단순한 이야기가 아닙니다. 더 현실적인 질문은 지금 수집된 암호문이 미래에 복호화될 수 있는가, 그리고 장기 기밀성이 필요한 데이터에 어떤 전환 계획이 있는가입니다.
Harvest Now, Decrypt Later
공격자는 오늘 복호화할 수 없는 트래픽도 저장해둘 수 있습니다. 나중에 충분한 양자 컴퓨팅 능력이나 새로운 공격 기법이 등장하면 과거 트래픽이 위험해질 수 있습니다. 장기 기밀성이 중요한 시스템은 지금부터 하이브리드 키 교환과 양자 내성 KEM 전환을 고려해야 합니다.
ML-KEM
NIST FIPS 203에서 표준화된 모듈 격자 기반 KEM입니다. 기존 ECC/DH 키 교환과 결합한 하이브리드 구조에서 자주 논의됩니다.
PQ3 같은 메시징 프로토콜
단일 알고리즘이 아니라 키 교환, 래칫, 장기 대화 보호, 침해 후 회복성을 함께 다루는 프로토콜 설계 관점으로 봐야 합니다.
9. 보안 점검 체크리스트
암호화 모듈, 프로토콜, 바이너리, 서버 API를 분석할 때 아래 항목을 순서대로 확인하세요.
| 점검 영역 | 핵심 확인 사항 |
|---|---|
| 키 생성 |
|
| 키 관리 |
|
| 운영 모드 |
|
| 인증 검증 |
|
| RSA 사용 |
|
| 비밀번호 |
|
| 리버싱/상호운용성 |
|
참고 기준
- RFC 8017: PKCS #1 v2.2 — RSA 암호화/서명 스킴 기준
- IETF CFRG RSA Guidance Draft — RSA 구현과 side-channel 방어 권고
- NIST FIPS 203 — ML-KEM 표준
- Apple PQ3 설명 — 하이브리드 양자 내성 메시징 프로토콜 사례