사전 X.509
표준

X.509

gabury1고친 사람 github-actions[bot]

X.509는 서로 모르는 프로그램끼리 같은 인증서를 같은 뜻으로 읽게 해 줍니다. 인증서에 어떤 칸을 어떤 이름으로 담을지, 받은 인증서를 어디까지 믿어도 되는지 가리는 절차까지 한 문서에 정해 둡니다. 웹 서버가 브라우저에 내미는 인증서가 이 표준을 따릅니다.

쉽고 빠른 이해

X.509는 인증서 한 장의 양식을 정한 표준입니다. 주소창의 자물쇠를 눌렀을 때 보이는 발급한 곳·유효 기간·누구의 인증서인지가 이 양식이 정해 둔 칸입니다.

이게 없으면 인증서를 내주는 곳과 읽는 곳이 서로 다른 양식을 쓰게 됩니다. 그러면 브라우저마다 읽어 낼 수 있는 인증서가 갈리고, 인증서를 받아 오는 곳을 바꿀 때마다 서버와 클라이언트를 함께 고쳐야 합니다.

이렇게 돕니다:

  1. 인증서에 담을 칸과 그 순서를 정합니다
  2. 나중에 새 정보를 덧붙일 수 있는 확장 구역을 열어 둡니다
  3. 받은 인증서를 발급자까지 거슬러 확인하는 절차를 정합니다

대가는 양식이 커서 다루기 번거롭다는 것입니다. 칸이 많고 칸 안에 칸이 또 들어 있는 데다 눈으로 읽을 수 없는 바이트로 저장되어, 내용을 보려면 따로 도구를 써야 합니다.

늘 필요한 것은 아닙니다. 서로를 이미 아는 두 쪽이 같은 비밀 키를 나눠 가졌다면 인증서는 필요 없습니다.

상세

X.509는 인증서 한 장의 양식을 정한 표준입니다. 인증서는 어떤 공개키가 누구의 것인지를 인증 기관이 서명해 보증한 문서입니다. X.509는 그 문서에 무엇을 어떤 이름으로 적을지를 정합니다.

세 겹으로 싸인 인증서 한 장

인증서 한 장은 세 덩이로 나뉩니다. 본문, 서명에 쓴 알고리즘 이름, 그리고 서명 값입니다.

이 인증서가 보증하는 공개키의 주인을 주체라고 부릅니다. 본문에 들어가는 칸은 판 번호(이 인증서가 몇째 판 양식으로 적혔는지)·일련번호·발급자 이름·유효 기간·주체 이름· 주체의 공개키, 그리고 확장(나중에 덧붙일 수 있게 열어 둔 칸들)입니다.

block-beta
columns 1
  block:TBS["서명이 덮는 본문"]
    columns 1
    A["판 번호 · 일련번호"]
    B["발급자 이름 · 유효 기간"]
    C["주체 이름 · 주체의 공개키"]
    D["확장 여럿"]
  end
  E["서명에 쓴 알고리즘 이름"]
  F["인증 기관이 붙인 서명 값"]

그림에서 볼 것은 서명이 본문 바깥에 있다는 점입니다. 서명은 본문 전체를 한 덩이로 보고 계산한 값이라, 본문에서 한 글자만 바뀌어도 서명이 안 맞습니다. 서명을 확인하려면 발급자의 공개키가 있어야 하고, 그 공개키는 다시 발급자의 인증서 안에 들어 있습니다.

양식을 한 문서로 정한 까닭

브라우저는 어느 인증 기관이 낸 인증서를 만날지 미리 모릅니다. 서버도 어떤 클라이언트가 붙을지 모릅니다. 양쪽이 같은 양식을 미리 알고 있어야 처음 보는 상대의 인증서를 읽어 낼 수 있습니다.

양식이 갈리면 인증서를 받아 오는 곳을 바꿀 때마다 서버와 클라이언트 코드를 함께 고쳐야 합니다. 표준은 그 대신 양식을 한 번 정해 두고, 서로 모르는 구현끼리도 같은 바이트를 같은 뜻으로 읽게 만듭니다.

이름의 내력도 여기에 남아 있습니다. X.509는 사람과 조직을 찾아보는 디렉터리 표준 X.500의 한 부속으로 나왔습니다. 주체 이름을 CN=example.com(CN 은 Common Name, 일반 이름) 처럼 적는 식별 이름 꼴이 그 내력에서 온 것입니다. 디렉터리 쪽은 널리 쓰이지 않았고 인증서 양식만 살아남았습니다.

판 셋과 v3 의 확장 구역

양식은 세 판을 거쳤습니다. 판마다 그전 판에서 못 하던 것이 하나씩 풀렸습니다.

판 더해진 칸 그전에 무엇이 곤란했나
v1 발급자·주체·공개키·유효 기간 같은 기본 칸 —
v2 발급자와 주체를 가리는 고유 식별자 같은 이름을 다른 주체가 다시 쓰면 구별이 안 됐습니다
v3 확장 구역 새 정보를 담으려면 표준 자체를 고쳐야 했습니다

오늘 쓰는 인증서는 거의 v3입니다. 셋째 판이 열어 둔 확장 구역 덕분에, 담을 정보가 늘어나도 표준을 다시 고치지 않고 확장을 하나 더 정의하면 됩니다.

확장이 하는 일

확장 하나는 인증서에 붙이는 이름표 하나입니다. 이름과 중요 표시와 값, 세 짝으로 이루어집니다.

중요 표시가 이 구조의 안전장치입니다. 모르는 확장을 만났을 때 무시해도 되는지 아니면 그 인증서를 거절해야 하는지를 확장 자신이 정해 둡니다. 새 제한을 모르는 옛 클라이언트가 그 제한을 못 본 채 통과시키는 일을 이렇게 막습니다.

flowchart TD
    A["확장 하나를 만난다"] --> B{"이 확장을 아나"}
    B -->|안다| C["값대로 처리한다"]
    B -->|모른다| D{"중요 표시가 붙었나"}
    D -->|붙었다| E["인증서를 거절한다"]
    D -->|안 붙었다| F["무시하고 넘어간다"]

갈림이 걸리는 곳은 오른쪽 아래 한 곳뿐입니다. 모르는 확장이라도 중요 표시가 없으면 그냥 넘어가고, 중요 표시가 붙어 있으면 인증서 전체가 거절됩니다.

자주 만나는 확장은 셋입니다.

확장 하는 일 이 칸을 안 보면
주체 대체 이름 이 인증서를 어떤 도메인 이름에 쓰는지 적습니다 요즘 클라이언트는 도메인을 맞춰 보지 못합니다
기본 제약 남의 인증서에 서명해도 되는 인증서인지 적습니다 서버 인증서를 가진 쪽이 다른 도메인 인증서를 발급합니다
키 용도 서명 검증용인지 키 교환용인지 적습니다 발급한 목적과 다른 곳에 인증서가 쓰입니다

첫 줄은 실무에서 제일 자주 걸립니다. 옛날에는 도메인 이름을 주체 이름 칸에 적었지만, 요즘 클라이언트는 주체 대체 이름 확장만 보고 도메인을 맞춥니다. 그 확장이 없는 인증서는 주체 이름이 맞아도 거절됩니다.

둘째 줄은 눈에 덜 띄지만 더 위험합니다. 남의 인증서에 서명할 수 있는 인증서와 그럴 수 없는 인증서를 가르는 칸이 이것뿐이라, 읽는 쪽이 이 칸을 안 보면 그 구분이 사라집니다.

셋째 줄은 같은 키를 여러 용도에 돌려쓰지 못하게 막습니다. 서명을 검증하라고 내준 키를 키 교환에 쓰려 하면 이 칸을 본 쪽이 걸러 냅니다.

구조를 적는 표기법과 바이트로 옮기는 규칙

X.509는 칸 구조를 ASN.1(Abstract Syntax Notation One, 추상 구문 표기법)이라는 표기법으로 적어 둡니다. ASN.1은 어떤 칸이 어떤 타입으로 들어가는지만 말합니다. 그 값을 바이트로 어떻게 늘어놓을지는 말하지 않습니다.

Certificate ::= SEQUENCE {
  tbsCertificate       TBSCertificate,
  signatureAlgorithm   AlgorithmIdentifier,
  signatureValue       BIT STRING
}

앞에서 본 세 겹이 이 세 줄입니다. 첫 줄이 서명이 덮는 본문이고, 둘째 줄이 서명에 쓴 알고리즘 이름, 셋째 줄이 서명 값입니다. 첫 줄 이름의 tbs 는 to be signed, 「서명될 대상」이라는 뜻입니다. 서명이 덮는 것이 이 덩이뿐이라는 말입니다.

바이트로 늘어놓는 규칙은 DER(Distinguished Encoding Rules, 구별 부호화 규칙)이 맡습니다. 같은 인증서를 언제나 같은 바이트로 적게 하는 것이 DER의 일입니다. 서명은 바이트 위에서 계산하므로, 적는 방법이 구현마다 갈리면 같은 인증서인데도 서명이 안 맞게 됩니다.

DER 바이트는 이진 데이터라 설정 파일이나 메일에 그대로 붙일 수 없습니다. 그래서 바이트를 Base64로 옮기고 머리말과 꼬리말 줄을 붙인 텍스트를 씁니다. 이 텍스트 형식이 PEM(Privacy-Enhanced Mail, 프라이버시 강화 메일)입니다.

-----BEGIN CERTIFICATE-----
(DER 바이트를 Base64 로 옮긴 줄이 이어집니다)
-----END CERTIFICATE-----

정리하면 층이 이렇게 쌓입니다.

flowchart TD
    A["X.509 가 정한 칸 구조"] --> B["ASN.1 로 적은 칸 정의"]
    B --> C["DER 로 늘어놓은 바이트열"]
    C --> D["그대로 저장한 이진 파일"]
    C --> E["Base64 로 옮겨 머리말을 붙인 PEM 텍스트"]

파일 확장자는 이 층과 따로 놉니다. .crt · .cer · .pem · .der 중 무엇이 붙어 있어도 안이 이진 바이트인지 PEM 텍스트인지는 열어 봐야 압니다. 인증서를 넘겨받아 붙였는데 읽히지 않는다면 대개 이 둘을 헷갈린 것입니다.

받은 인증서의 검증 절차

이 표준은 양식만 정하고 끝나지 않습니다. 받은 인증서를 검증하는 절차도 함께 정합니다.

인증서는 발급자를 따라 위로 이어집니다. 서버 인증서는 중간 인증서가 서명하고, 중간 인증서는 미리 믿기로 정해 둔 루트 인증서가 서명합니다. 이렇게 루트까지 거슬러 이어 붙인 것을 인증서 체인이라고 합니다.

flowchart TD
    subgraph 체인["인증서 체인"]
        S["서버 인증서"]
        M["중간 인증서"]
        R["루트 인증서 · 미리 믿기로 정해 둔 것"]
    end
    S -->|발급자를 따라 거슬러 올라감| M
    M -->|발급자를 따라 거슬러 올라감| R
    R -.->|서명해 준다| M
    M -.->|서명해 준다| S

실선은 읽는 쪽이 거슬러 올라가는 방향이고, 점선은 서명이 내려온 방향입니다. 두 방향이 반대라서, 맨 아래 인증서 한 장을 받아도 맨 위까지 확인이 이어집니다.

인증서 체인의 이음매마다 네 가지를 확인합니다.

  • 발급자의 공개키로 서명이 맞는지
  • 유효 기간 안인지
  • 남의 인증서에 서명할 권한이 있는지
  • 기간이 끝나기 전에 취소된 것은 아닌지

이 확인 절차를 경로 검증이라고 합니다.

X.509 자체는 넓게 열려 있어서 구현마다 다른 선택을 할 여지가 남습니다. 인터넷에서 쓰는 인증서는 RFC(Request for Comments, 인터넷 표준 문서) 5280이 그 여지를 좁힙니다. 어떤 확장을 반드시 처리해야 하는지, 경로 검증의 각 단계에서 무엇을 봐야 하는지를 이 문서가 정해 둡니다.

기간이 끝나기 전에 인증서를 못 쓰게 만드는 인증서 폐기도 같은 표준 안에 있습니다. 못 쓰게 된 인증서의 일련번호를 모아 인증 기관이 서명해 내놓는 목록이 CRL(Certificate Revocation List, 인증서 폐기 목록)입니다. 이 목록도 인증서와 같은 양식 규칙 위에 서 있어서 읽는 방법이 같습니다.

이 표준이 정하지 않는 것

어떤 뿌리를 믿을지는 정하지 않습니다. 어느 루트 인증서를 믿을지는 운영체제와 브라우저가 들고 다니는 신뢰 저장소 목록이 정합니다. 표준은 인증서 체인을 어떻게 따라가고 무엇을 확인할지만 말합니다.

도메인의 주인이 맞는지 확인하는 방법도 정하지 않습니다. 발급 전에 신청자를 어떻게 심사할지는 인증 기관의 운영 규칙입니다. 표준은 심사를 마친 뒤 내주는 문서의 모양만 정합니다.

인증서를 받아 오는 대화도 다른 규격의 몫입니다. 키를 만들고 인증서 서명 요청을 보내 인증서를 받아 오고 기간이 끝나기 전에 갱신하는 절차는 X.509 밖에 있습니다.

쓰는 곳과 안 쓰는 곳

웹 서버에 인증서를 붙일 때 만납니다. TLS(Transport Layer Security, 전송 계층 보안)로 붙는 연결에서 서버가 내미는 인증서가 이 양식입니다. 서비스끼리 서로 인증서를 내미는 mTLS(mutual TLS, 상호 TLS) 연결에서도 같은 양식을 씁니다. 사내 인증 기관을 세워 내부 서비스에 인증서를 나눠 줄 때, 배포하는 프로그램에 서명할 때도 마찬가지입니다.

반대로 두 쪽이 이미 같은 비밀 키를 나눠 가졌다면 인증서가 필요 없습니다. 처음 보는 상대의 공개키를 믿을 근거를 만드는 것이 인증서의 일이라, 서로를 이미 아는 사이에서는 양식과 체인 검증이 손만 늘립니다. SSH 키(SSH 는 Secure Shell, 보안 셸)처럼 인증서를 거치지 않고 공개키를 서버에 미리 등록해 두는 방식도 흔합니다.

관련 항목

X.509 인증서 한 장을 이루는 칸과 확장

식별 이름 · 주체 대체 이름 · 기본 제약 · 키 용도 · 공개키 · 디지털 서명

X.509 구조를 바이트와 텍스트로 옮기는 부호화 규칙

ASN.1 · DER · BER · PEM · Base64

X.509 인증서를 발급하고 신뢰를 세우는 주체

인증 기관 · 루트 인증서 · 중간 인증서 · 신뢰 저장소 · 인증서 체인 · 자체 서명 인증서

X.509 인증서를 못 쓰게 만드는 절차와 확인 수단

인증서 폐기 · CRL · OCSP · OCSP 스테이플링 · 인증서 투명성

X.509 인증서를 실제로 주고받는 프로토콜

TLS · HTTPS · mTLS · 클라이언트 인증서 · 코드 서명

X.509 인증서를 발급받고 갱신하는 작업

인증서 서명 요청 · ACME · Let's Encrypt · 인증서 갱신 · 개인키

X.509 가 기대는 암호 기술

공개 키 암호 · 해시 함수 · RSA · 타원 곡선 암호 · SHA-1

X.509 가 비롯된 상위 표준과 이웃 표준

X.500 · X.501 · 공개 키 기반구조 · 경로 검증 · LDAP

X.509 검증이 막았을 때 나는 오류

호스트 이름 불일치 · 만료된 인증서 · 신뢰할 수 없는 발급자 · 인증서 검증 실패

X.509 대신 쓰는 다른 신원 확인 수단

SSH 키 · JWT · 사전 공유 키 · API 키 · 인증서 핀닝

다른 이름: X.509v3 · X.509 인증서