최종 수정일: 2025년 5월 25일
들어가며
인증서와 공개키 기반구조(PKI)는 어렵습니다. 이 부분을 적극적으로 파고들지 않고 피하는 똑똑한 분들을 많이 봤습니다. 저 역시 오랜 시간 외면해왔다가, 필요에 의해 배우기 시작했고, 더 빨리 시작하지 않은 것이 후회될 정도로 강력하고 흥미로운 분야임을 알게 되었습니다.
PKI는 시스템을 암호학적으로 정의할 수 있게 해줍니다. 어디든, 어떤 플랫폼에서든, 벤더에 관계없이, 각 부분이 서로 안전하게 통신할 수 있게 해주는 매우 유연한 설계방식입니다. TLS를 쉽게 쓰고 VPN 없이도 안전한 네트워킹이 가능한 이유가 바로 PKI 덕분입니다. 핵심 개념은 생각보다 단순하지만 문서화가 엉성해서 처음엔 어렵게 느껴집니다.
이 글은 개발자와 실무자가 PKI의 중요한 개념과 흔한 함정들을 빠르게 파악할 수 있게 돕는 "누락된 매뉴얼"을 지향합니다.
제가 예제로 사용하는 도구는 오픈소스 프로젝트 step CLI와 step certificates입니다. 따라하려면 brew install step으로 설치할 수 있으며, 별도 Certificate Manager로도 체험이 가능합니다.
정말 한 줄 요약
**PKI와 인증서의 목표는 "이름(식별자)과 공개키를 연결하는 것"**입니다. 나머지는 구현 세부사항에 불과합니다.
PKI 용어 한눈에 살펴보기
앞서 이야기할 기술 용어들을 정의합니다.
- 엔터티(entity): 존재하는 모든 것(물리적/논리적). 컴퓨터, 코드, 나, 점심으로 먹은 부리토, 상상 속 유령까지 모두 엔터티입니다.
- 아이덴티티(identity): 엔터티를 정의하는 속성 모음입니다. 소속, 나이, 위치, 즐겨 입는 신발 사이즈 등. **식별자(identifier, name)**는 아이덴티티와 다르며, 고유하게 엔터티를 가리키는 참조입니다.
- 주장(claim): 엔터티가 자신에 대해(혹은 타인에 대해) 무언가라고 내세우는 것. "나는 Mike입니다", "내 나이는 33살입니다" 등.
- 인증(authentication): 어떤 주장이 사실인지 검증하는 과정입니다.
- 가입자/최종 엔터티(subscriber/end entity): PKI에 참여하며 인증서의 주체(subject)가 될 수 있는 엔터티
- 인증기관(CA, certificate authority): 인증서를 발급하는 엔터티 또는 조직
- 루트 인증서/중간 인증서(root/intermediate certificate): CA에 속하며, 신뢰사슬을 구성할 때 사용됨
- 최종 엔터티 인증서(leaf certificate): 실제 서비스나 코드 등에 부여되는 인증서
- 신뢰자(relying party): 인증서를 검증하고 신뢰하는 엔터티
이제부터 실제로 크립토그래피가 어떻게 동작하는지 살펴보겠습니다.
MAC과 서명: 인증의 시작
- MAC(Message Authentication Code): 메시지와 비밀키(패스워드)를 해시함수에 넣어 생성되는 검증 데이터. 송신자와 수신자가 같은 비밀키를 알아야 하므로, 유출에 취약. (자체 구현 말고 HMAC을 쓰세요)
- 서명(signature): 공개키 암호방식 사용. 비밀키 소유자만 서명을 만들 수 있고, 공개키만으로 검증.
(비밀키만 알면 누가 서명했는지 입증 가능 → 부인방지성(non-repudiation))
MAC은 상대방을 특정할 수 없음. 서명은 특정 엔터티(비밀키 소유자)만 서명할 수 있습니다. 실제 세계 서명과 비슷하게 동작합니다.
공개키 암호: 컴퓨터가 보는 법
- 공개키 암호(비대칭 암호): 공개키/비밀키 쌍이 있고, 공개키로 암호화/비밀키로 복호화, 비밀키로 서명/공개키로 검증
공개키 시스템의 핵심은 _비밀키 노출 방지_입니다.
공개키 암호의 기적은 "네트워크 상에서 직접 비밀정보를 주고받지 않아도 신뢰할 수 있는 증거가 되는 것"입니다. 예를 들어, 상대방(public key 소유)을 알면 임의값에 서명하게 해서 상대방의 정체성을 입증할 수 있습니다.
인증서: 컴퓨터와 코드의 신분증
- 인증서: "이름"과 "공개키"가 포함된 데이터 구조체이며, CA(issuer)가 서명합니다. 서명이름과 공개키의 결합을 신뢰사슬 기반으로 보장.
- "A 인증기관이 Bob에게 인증서 발급" => "A가 서명한 Bob의 공개키는 이 값이다"는 주장
- DMV의 운전면허증 혹은 여권과 유사한 역할
- 인증서에는 CA 여부, 사용 용도(서명/암호화 등), 만료 기간 등 다양한 정보가 들어가지만 본질은 불변: 공개키와 이름의 결합
인증서의 비트와 바이트: X.509, ASN.1, OIDs, DER, PEM, PKCS
-
X.509 v3 인증서: 웹(HTTPS)에 가장 널리 쓰임. PKI X.509 인증서는 ASN.1 포맷으로 정의되고, 일반적으로 DER(바이너리) 또는 PEM(베이스64 인코딩)으로 직렬화됨.
-
ASN.1: 데이터 타입을 정의하는 표준(마치 JSON/Protobuf/SQDDL과 유사). 이름, 공개키, 서명 등.
-
OID: ASN.1에서 타입 지정에 사용하는 계층적 숫자(예: 2.5.4.3 = X.509 common name)
-
DER: ASN.1의 가장 흔한 인코딩 규칙(바이너리)
-
PEM: 베이스64 인코딩(-----BEGIN CERTIFICATE-----로 시작)
- 확장자: .pem, .crt, .cer(PEM), .der(DER)
-
PKCS#7, PKCS#12: 인증서(및 키)를 아우르는 envelope 포맷. .p7b/.p7c(Java), .pfx/.p12(MS)
-
PKCS#8: 비밀키를 표현하는 ASN.1 구조
요약: 인증서/키(ASN.1) → DER(바이너리), PEM(가독성 인코딩) 등으로 직렬화
공개키 인프라(PKI)의 실제
- PKI(Public Key Infrastructure): 인증서/키의 발급, 배포, 저장, 검증, 폐기, 회수 등을 포괄
- 내부 PKI: 기업이나 조직용 자체 발급 시스템(프로덕션 인프라, 엔드포인트 등 내부 리소스 식별용)
- 웹 PKI: 공개 인터넷(웹 브라우저–HTTPS)에서 쓰이는 PKI(예: Let's Encrypt)
- PKI의 본질: 공개키와 이름을 바인딩
웹 PKI vs 내부 PKI
- 웹 PKI: 브라우저에 내장된 루트 인증기관(CA) 신뢰 목록 사용, 공인발급기관(CA/B Forum, PKIX/RFC 5280 기준). 공개 웹서비스 및 API 권장.
- 내부 PKI: 조직 내에 자체 CA 구축, 자체 신뢰 목록 및 정책 수립. 내부 시스템, 사설 자원 등에 사용.
내부 PKI가 필요한 이유:
- 웹 PKI는 내부 IP/DNS 등 사설 식별자 작업 못함(공인 DNS만 허용)
- 대량 발급, 라이프사이클/자동화 통제, 다양한 이름바인딩 불가
신뢰와 신뢰성
- 신뢰 저장소(Trust Store): 브라우저/OS/언어 등에서 미리 내장된 신뢰 루트 인증서 목록
- 이 목록 개인정보주체, 앱, 서버, 동작환경 등에 따라 다름(Mac=Keychain, Linux=/etc/ssl...)
- 루트 인증서: 일반적으로 자기서명(self-signed), 신뢰저장소에 등록되어 사슬의 뿌리가 됨
- 웹 PKI 루트 프로그램: Apple, Microsoft, Mozilla(Firefox), Google(Chrome) 등이 주도
CA의 신뢰성과 한계
- CA는 브라우저/OS 등에서 디폴트로 신뢰되지만, 전적으로 믿을 만한가?
- 정부/기관 사칭, CA 해킹, 잘못된 인증서 발급 등 경험 다수(중국, NSA, DigiNotar, Comodo 등 사건)
- 웹 PKI의 총체적 신뢰성은 가장 취약한 CA에 달림(즉, 최근 이슈도 많음)
연합(Federation)과 상호 운용
- 웹 PKI는 모든 신뢰저장소에 있는 CA가 모든 이름에 대해 인증서 발급이 가능(최약체 CA가 전체 보안 좌우)
- DigiNotar 사건, Sennheiser 사건 등
- 완화책: CAA(DNS에 CA 허용 기록), CT(인증서 투명성 로그), HPKP(핀 방식) 등. RP(검증자)가 실행해야 실효 있음.
CA(인증기관), 중간 CA, 인증서 체인, 번들링
- 루트 CA 키는 오프라인/하드웨어 보안관리에 두고, 자동화 위해 중간나머지 CA(Intermediate CA)가 리프 인증서(Leaf Certificate) 발급/서명
- 체인(chain): 루트-중간-리프 (CA 계층)
- 번들(bundle): 리프와 중간 인증서를 한 번에 묶어서 제공(PEM 등)
- 아파치/Nginx 등 대부분 서버 설정 시 리프 인증서와 중간 번들을 함께 입력
- Windows/Java 등은 PKCS#7, #12 포맷도 활용(웹은 PEM 번들이 일반적)
인증서 경로 검증
- 클라이언트(브라우저 등)는 서버(구독자)가 전송한 리프, 중간 인증서로부터 루트까지 유효성을 "경로 검증"함
- 만료, 정책, 키사용 제한, 폐기 여부 등도 함께 검사함
-k(curl 등)로 검증 생략 금지(인증 없는 암호화는 무의미)
키/인증서 라이프사이클
이름 지정과 SAN
- 본래 X.509는 X.500 DN(구별이름, distinguished name)을 사용했으나, 요즘은 이름에는 SAN(Subject Alternative Name) 확장 사용이 표준. SAN에는 DNS, EMAIL, IP, URI 등 네 가지 형식 활용
- SAN = 코드/서버/서비스는 DNS, 사람은 EMAIL, 기타 URI
키 쌍 생성
- 엔터티(구독자)가 직접 공개키/비밀키 쌍 생성이 최선(비밀키 노출 방지)
- RSA(최소 2048bit, PSS 방식) 권장. 점진적으로 ECDSA/EdDSA 권장(추천: P-256, Curve25519)
- openssl, step CLI 등으로 손쉽게 생성 가능
발급 요청
- CSR(Certificate Signing Request): 공개키와 이름 포함, 비밀키로 자가서명 후 CA에 제출(PKCS#10, ASN.1)
신원 증명(Identity proofing)
- Web PKI: DV(도메인 인증), OV(조직 인증), EV(확장 인증)
- DV는 도메인 소유 증명(WHOIS, DNS, HTTP 챌린지 등으로)
- OV/EV는 법인의 공공 기록, 공식 인증 필요(주로 수동 심사)
- 내부 PKI: Chef/Puppet/K8s 등 기존 인프라 신뢰체계 활용
만료와 갱신(Expiration & Renewal)
- 인증서는 만료일(issued at, not before, not after) 포함, 영구 인증서는 위험함(취약점 노출 확률이 시간에 비례함)
- 만료 후(혹은 이전에) 갱신/재발급 필요. 대부분 CA는 "새 인증서 발급" 처리(갱신=발급과 동등)
- 자동화/모니터링 필수(짧은 유효기간/Let’s Encrypt 기준 90일, 내부 PKI라면 24시간 이하, 5분 등 더 짧게도 가능)
폐기(Revocation)
- 비밀키 유출, 노출, 서비스 불필요 등 사유로 폐기(즉시 신뢰 중단 필요)
- 전통적 방법: CRL(폐기 목록)/OCSP(온라인 폐기 상태 확인)
- 둘 다 RP가 외부에서 확인해야 하고, 잘 작동하지 않거나(느림, 장애, 프라이버시 노출 등 문제), 대다수는 생략(Default: 폐기된 인증서도 허용)
- 대안: 패시브 폐기(passive revocation) = 짧은 수명 인증서 발급, 갱신만 막으면 자동 폐기
인증서 실제 사용 예
- RP(검증자): 신뢰할 루트 인증서 지정
- 구독자(서버 등): 자신의 인증서와 비밀키 지정(혹은 키/CSR 자동생성)
- 상호 인증(Mutual TLS) 등에서는 둘 다 필요
- 대부분의 TLS 서버/클라이언트는 이 방식 지원
- 실무의 복잡성은 주로 "인증서와 키의 수명 및 배포/갱신 자동화"에 있습니다.
요약
- 공개키 암호화는 네트워크상에서도 ID 확인을 가능하게 한다
- 인증서는 공개키와 이름의 결합(신원증명에 사용). CA(인증기관)는 민간 DMV와 비슷한 역할
- 현실 세계의 대부분 인증서는 X.509, 계층 구조(PKI), PEM 인코딩
- Web PKI: 공인 CA 기반, 브라우저/OS 기본 신뢰
- 내부 PKI: 자체 CA, 자동화 및 통제 강화. 조직 내부자원 인증/보안에 권장
- 이름은 SAN(DNS/EMAIL/URI 등)으로, 키는 ECDSA/RSA로, CA에는 CSR 제출
- 패시브 폐기와 자동화된 갱신, 경로 검증 비활성화 금지
- PKI의 본질: 이름을 공개키에 바인딩하는 것
저자 Mike Malone은 Smallstep의 CEO로 6년간 인프라 보안 자동화에 매진해왔으며, Betable CTO, 분산 시스템 연구-구현, 논문 저자로 활동한 경력이 있습니다.