최근 수정 시각 : 2023-04-22 05:42:46

PEM


1. 개요2. 역사3. 상세
3.1. 형식
3.1.1. 예시
3.2. 확장자
4. 관련 문서

1. 개요

Privacy Enhanced Mail, PEM

각종 인증 및 비대칭키 암호화 알고리즘에서 사용되는 비밀키, 인증서 등을 텍스트 형식으로 직렬화하여 저장하기 위한 파일 포맷.

2. 역사

처음부터 비밀키/인증서 직렬화를 위해 만들어진 형식은 아니고, 원래 기원은 조금 생뚱맞게도 이메일을 안전하게 주고받기 위한 IETF의 1993년 표준(IETF-1421) "privacy-enhanced mail" 에 정의된 파일 형식이다.[1]

해당 표준은 상업적으로 성공하지 못해 잊혀졌지만, PEM만은 PGP, SSL/TLS, 그리고 이를 기반으로 하는 SSH 등에 널리 수입되며 살아남았다. 워낙 낡은 표준인지라 결국 2015년 IETF-7468 이라는 이름으로 새로 작성되었다.

3. 상세

흔히 알고 있는 쓰임과는 무관하게, 실제로는 어떤 종류의 바이너리 데이터든 저장할 수 있다. 해당 포맷의 핵심이 BASE64이기 때문.

대부분의 암호화 표준이 ASN.1과 DER을 사용해 키를 표현하지만, DER이 바이너리 형식이기 때문에 텍스트 매체만을 사용해서 주고받기 어렵다는 단점이 있다.[2] 따라서 PEM은 전부 출력 가능한 아스키 문자만을 사용해서 키를 저장할 수 있다는 장점을 가지게 된다.

3.1. 형식

PEM 포맷은 크게 세 부분으로 구성된다.
  • Header: -----BEGIN <데이터 라벨>-----. -이 양쪽으로 다섯 개임에 주의.
  • Body: BASE64로 인코딩한 데이터가 들어간다. 몇 글자마다 줄바꿈이 들어가는지는 표준에 명시되어있지 않지만 OpenSSL은 보통 64자, OpenSSH은 70자 단위로 출력한다.
  • Footer: -----END <데이터 라벨>-----. 라벨은 헤더에서 시작한 값과 같아야 한다.
이때, 라벨은 해당 데이터의 종류를 나타낸다. 대표적인 예시로는 PRIVATE KEY(비밀키), PUBLIC KEY(공개키), CERTIFICATE REQUEST(인증서 발급 요청), CERTIFICATE(인증서), OPENSSH PRIVATE KEY(OpenSSH 비밀키), 등이 존재한다. 앞서 언급한 대로 PEM을 단순히 바이너리 데이터 저장용으로 사용한다면 이곳에 적절한 라벨을 직접 적어도 된다. 표준상 -(U+002D)를 제외한 아스키 범위의 모든 출력 가능한 문자[3](U+0021~U+007E)를 사용할 수 있지만 일반적으로 알파벳 대문자만 사용한다.

또한 필요에 따라 한 PEM 파일 안에 여러 PEM 데이터를 넣을 수도 있다. 대표적인 예시가 현재 인증서부터 root CA까지의 모든 인증서 정보를 포함한 fullchain file.[4] 문서와 파일이 1:1 관계가 아니라는 점에서 YAML과 비슷하다.

3.1.1. 예시

namu.wiki라는 도메인을 가지고 uman.le라는 self-signed CA로부터 SSL인증서를 받는 과정을 생각해 보자.

OpenSSL을 사용해 생성한 2048비트 길이의 RSA 비밀키 예시.[5]
#!syntax sh
openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 -out namu_wiki.key
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCqDz5Qo0/v6UUx
IdC3Nl/1We8Q1v+557o1t18VpMobUx8R50rS6OsuedAobaBWCcx5l3kMl14Nmb2r
c/a75OiZaUnq6GlgEjBV9PT1q65nUCyALN7LoePeO4ZaBntOgSHmLVeje1ZO7H+X
FLATmURsO+u56r4qJ5Dtv7hA/W4jLgzybfJSk1/JqOpC6iVU4D4GhUfCc+U09Jm7
QBU+21718gRi0oHzmPqXRtLW4KA7uy+Mjt1RB/cPUStwqAK4oFlX1wkCbdUG3SVP
WufOLLsTpxI44la2fy9+XXUbRHsne+9QPiVlhZTtA9uIFSiKOHSsV0zlpgXn5o35
mwcPD0sZAgMBAAECggEAASwlZgFCPKld40FUQuHFQUKd+H6Vo8P6ZNgUopMQxlBz
5I001wW+aAMfUXAJYh5415ZuQg0hxl6QJV2heVkngLXuSbLRgH/k78Abwj0tRiQS
MniyDohZXapeZOzU2SKLHR756DQiUQf7YeDFd//KA3mu8zNa8p0e4tdmtKKqqY5r
AQoaiRksrFPAa/m54h5veojUFyyKk4wpqw11NZBNVnL16I0r26MZ/ZS6wLO/QK9J
PKRBfGe5IYE5B64wWZz64Ns0/7d7jEmfuE9+M4tQ3IsFdHmRXVtHken7PFSunU3Y
lDK/Ozchh4z2w46pCA+mvpiLhwbZKeZt/PNTol0KgQKBgQDCUO3XW7Xtiu8maj2U
fzdZD1hGnnJrBnx+5YpvFVVGwlKV3OYu2ulOJhRBJEAN38+nXfxA+zld+s0U8zKM
AeC5XhgtwfPddA5DsT2KZRnA/S5y9WivBbcbUTWlFo37BHq8CFveO8jYEBlx7Jn5
0fcH4LKTJicICpfz8KgiYTkT4QKBgQDgCxtOUHRhb36Cv/GslyfA0NoeUXJGGadK
xxiO02WqnWeD87M+5wn4izrg9p30Cdac5zAMPPKmXC1JcVebRqlwSWVrNN88Kl2P
mFQIfyk4rjE77N6xFPwOlvuI1IF2OrqiBbFPBaYHvNAr7reRRhSWHDeWxKpqgdiI
ccbXQbWeOQKBgQCYC1sPN+OSizO1i0vD9gcI2Mjp+PDubZTcdh9r6/Vd+I9GVCKI
ZyJG0+TlU8gLEyUYgKHw9qJctvHhgqn6gz2jzcx9gVWf4j+HGBhfQdx09aZyrTPM
P1yXM2QWWR+fWlHu71ty+LSe23oNemTA5Vm2AEu6eA/yatebOKNWw1w2AQKBgGJ3
HyodG/kSqlRGja2fxR3t7F70xrdeYPLxa66h5AXHJg6NFZMosW4lqtviJ2tww3yz
8p7+TlqlGlYz1R/uP1uVNliWpSHpMLOCbDRLxs8dZ5ABu6GyKlEzNBtf4gIXL50z
yUiEoAK4jfkw8kaLuVJ71kaR6p/ir0rQTmaef3apAoGARpMuYivBXLwb5h3E3XAe
zTimXlFEJSRRtZv9lxjbv4h8uSO5p6loK8hyaBwQ2zWtWKuHVS1TdhgNtL29uehH
HgPhljkoZQ1cI2opPVwbznG5aCcjaNMRfeQmbN037/aWciS3WwxtM4Ayn6apZPXM
hOd+icEvFHFpU1N2PAnyHsg=
-----END PRIVATE KEY-----

uman.le CA에서 namu.wiki측의 CSR을 받아들여 발급한 인증서 예시
#!syntax sh
openssl req -x509 -nodes -newkey rsa:2048 -keyout ca.key -out ca.crt -subj "/C=PY/ST=Capital/L=Asunción/O=umanle S.R.L./CN=uman.le"
openssl req -new -key namu_wiki.key -out namu_wiki.csr -subj "/C=PY/ST=Capital/L=Asunción/O=Namu Wiki/CN=namu.wiki"
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in namu_wiki.csr -out namu_wiki.crt
-----BEGIN CERTIFICATE-----
MIIDQzCCAisCFHBmAtMo1W6ZTN0EaNC2k7q6vKuZMA0GCSqGSIb3DQEBCwUAMF8x
CzAJBgNVBAYTAlBZMRAwDgYDVQQIDAdDYXBpdGFsMRQwEgYDVQQHDAtBc3VuY2nD
g8KzbjEWMBQGA1UECgwNdW1hbmxlIFMuUi5MLjEQMA4GA1UEAwwHdW1hbi5sZTAe
Fw0yMzAzMjkxMTU1MDRaFw0yMzA0MjgxMTU1MDRaMF0xCzAJBgNVBAYTAlBZMRAw
DgYDVQQIDAdDYXBpdGFsMRQwEgYDVQQHDAtBc3VuY2nDg8KzbjESMBAGA1UECgwJ
TmFtdSBXaWtpMRIwEAYDVQQDDAluYW11Lndpa2kwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQCqDz5Qo0/v6UUxIdC3Nl/1We8Q1v+557o1t18VpMobUx8R
50rS6OsuedAobaBWCcx5l3kMl14Nmb2rc/a75OiZaUnq6GlgEjBV9PT1q65nUCyA
LN7LoePeO4ZaBntOgSHmLVeje1ZO7H+XFLATmURsO+u56r4qJ5Dtv7hA/W4jLgzy
bfJSk1/JqOpC6iVU4D4GhUfCc+U09Jm7QBU+21718gRi0oHzmPqXRtLW4KA7uy+M
jt1RB/cPUStwqAK4oFlX1wkCbdUG3SVPWufOLLsTpxI44la2fy9+XXUbRHsne+9Q
PiVlhZTtA9uIFSiKOHSsV0zlpgXn5o35mwcPD0sZAgMBAAEwDQYJKoZIhvcNAQEL
BQADggEBAEtyoPaF0NwfYBp7G11z55iGIyUp03VCAeJRTeSjJzoqUqZ80QxDzo7M
RcOt0cVHe/OPM/pEOfFK12D1ictOy2y1ppkAm2ggLEP3eGxqK099afCleROFXvhP
CK46hhS5svAYL92/W5HyoRqadYjYkbTjHq3n+/A11WgU7WM2pa2hsX72SrB04ise
yDt+AmmVADBQlX0hH5IdZdp+s9idHBec66jJBwgyLkVirSWCipWJgW8dd3r1Vvo2
NlHxgXHe8jyzTHGPWjFytKCd0+xZqSLQwEp4Gpt2yi+WEOJjwAqL8TouLk6ZHF3L
X+2Mmq3dO4C8OVlFi0IN1NV2X5CE1nM=
-----END CERTIFICATE-----

3.2. 확장자

가장 기본적인 확장자는 *.pem이지만, 워낙 base64가 다 비슷비슷하게 생겼고 라벨도 파일을 열지 않으면 읽기 어렵기 때문에 파일의 내용별로 확장자를 다르게 적는 관습이 있다. 문제는, 이 관습은 표준이 없이 그냥 제각각 편한 대로 쓴다는 점. TLSOpenSSL을 공부하는 초심자라면 이 문서 저 문서 모두 같은 내용을 다루는데 확장자가 *.pem, *.crt, *.key, *.csr 심지어 *.p7b 등으로 다 달라 머릿속이 혼파망 상태가 되기 쉽다.

결론적으로 말하자면, 확장자만 보고서는 해당 파일의 종류를 알 수 없다. 심지어 한 파일 안에 서로 다른 타입의 내용이 동시에 들어있을 수도 있다! 반드시 파일을 열어서 라벨을 확인하거나 유닉스 기반 서버에 있다면 file 명령어로 파일 타입을 확인하자. 아래는 '일반적으로' OpenSSL에서 사용되는 확장자와 그 종류를 정리한 목록이다.
  • *.pem: 흔히 공개키, 인증서 또는 인증서 신청 등으로 생각할 수 있지만 사실 모든 PEM 형식에 다 쓸 수 있어 무조건 열어보지 않고는 알 수 없는 녀석 1순위이다.
  • *.key: 일반적으로 비밀키를 의미한다. 주의할 점이, OpenSSH는 이와는 전혀 무관하게 아예 확장자를 달지 않는다.
  • *.cer, *.crt: X.509 형식의 인증서. 이론상으론 DER 인코딩된 바이너리 파일이지만, PEM 형식으로 저장하고 해당 확장자를 다는 일도 굉장히 흔하다. Microsoft Windows의 경우 *.crt 확장자를 더블클릭하면 윈도우 내부 root 인증서 저장소로 임포트하지만, *.cer의 경우 단순히 해당 인증서를 읽기 전용으로 보여준다.
  • *.csr: 인증서 발급 요청.
  • *.p7b, *.p7c: PKCS#7 형식의 인증서. PKCS7 라벨을 가진다.

아래는 OpenSSL에서 사용되지 않지만 다른 분야에서 자주 쓰이는 PEM 파일 확장자들의 목록이다.
  • *.pub: OpenSSH 공개키. OpenSSH 외에서는 자주 쓰이지 않는다.
  • *.gpg, *.pgp, *.asc: GnuPG 및 기타 PGP로 암호화된/서명된 메시지(PGP MESSAGE) 또는 공개키(PGP PUBLIC KEY BLOCK). 다만 --armor 옵션을 넣지 않은 경우 기본적으로 바이너리로 출력되니 결국 파일을 열어보아야 알 수 있다.

4. 관련 문서


[1] Privacy-Enhanced Mail (PEM) is a de facto file format for storing and sending cryptographic keys, certificates, and other data, based on a set of 1993 IETF standards defining "privacy-enhanced mail." While the original standards were never broadly adopted and were supplanted by PGP and S/MIME, the textual encoding they defined became very popular. - Wikipedia[2] 애당초 해당 표준이 나오게 된 계기가 메일 전송시 키를 첨부하기 위해서였다.[3] any printable character except hyphen-minus https://www.rfc-editor.org/rfc/rfc7468[4] Let's Encrypt 등에서 주는 fullchain.pem 파일이 바로 이것이다.[5] 인증서는 모두가 볼 수 있기 때문에 해당 텍스트를 그대로 복사해서 메모장 등에 뭍혀넣어 저장하고 openssl x509 -noout -text -in namu_wiki.crt를 실행하면 앞서 입력한 CA 정보 등을 확인할 수 있다.