[[분류:파일 형식]][[분류:컴퓨터 보안]][[분류:암호학]] [목차] == 개요 == {{{+1 Privacy Enhanced Mail, PEM}}} 각종 인증 및 [[비대칭키 암호화]] 알고리즘에서 사용되는 비밀키, [[인증서]] 등을 텍스트 형식으로 [[데이터 직렬화 형식|직렬화]]하여 저장하기 위한 파일 포맷. == 역사 == 처음부터 비밀키/인증서 직렬화를 위해 만들어진 형식은 아니고, 원래 기원은 조금 생뚱맞게도 [[이메일]]을 안전하게 주고받기 위한 [[https://www.rfc-editor.org/rfc/rfc1421|IETF의 1993년 표준(IETF-1421)]] "privacy-enhanced mail" 에 정의된 파일 형식이다.[* 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. - [[https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail|Wikipedia]]] 해당 표준은 상업적으로 성공하지 못해 잊혀졌지만, PEM만은 [[PGP]], [[SSL]]/[[TLS]], 그리고 이를 기반으로 하는 [[SSH]] 등에 널리 수입되며 살아남았다. 워낙 낡은 표준인지라 결국 2015년 [[https://www.rfc-editor.org/rfc/rfc7468#section-1|IETF-7468]] 이라는 이름으로 새로 작성되었다. == 상세 == 흔히 알고 있는 쓰임과는 무관하게, 실제로는 어떤 종류의 바이너리 데이터든 저장할 수 있다. 해당 포맷의 핵심이 [[BASE64]]이기 때문. 대부분의 암호화 표준이 ASN.1과 DER을 사용해 키를 표현하지만, DER이 바이너리 형식이기 때문에 텍스트 매체만을 사용해서 주고받기 어렵다는 단점이 있다.[* 애당초 해당 표준이 나오게 된 계기가 메일 전송시 키를 첨부하기 위해서였다.] 따라서 PEM은 전부 출력 가능한 [[아스키 코드|아스키 문자]]만을 사용해서 키를 저장할 수 있다는 장점을 가지게 된다. === 형식 === 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)를 제외한 아스키 범위의 모든 출력 가능한 문자[* any printable character except hyphen-minus [[https://www.rfc-editor.org/rfc/rfc7468#section-3]]](U+0021~U+007E)를 사용할 수 있지만 일반적으로 알파벳 대문자만 사용한다. 또한 필요에 따라 한 PEM 파일 안에 여러 PEM 데이터를 넣을 수도 있다. 대표적인 예시가 현재 인증서부터 root CA까지의 모든 인증서 정보를 포함한 fullchain file.[* [[Let's Encrypt]] 등에서 주는 {{{fullchain.pem}}} 파일이 바로 이것이다.] 문서와 파일이 1:1 관계가 아니라는 점에서 [[YAML]]과 비슷하다. ==== 예시 ==== {{{namu.wiki}}}라는 도메인을 가지고 {{{uman.le}}}라는 self-signed CA로부터 [[SSL]]인증서를 받는 과정을 생각해 보자. [[OpenSSL]]을 사용해 생성한 2048비트 길이의 [[RSA 암호화|RSA]] 비밀키 예시.[* 인증서는 모두가 볼 수 있기 때문에 해당 텍스트를 그대로 복사해서 메모장 등에 뭍혀넣어 저장하고 {{{openssl x509 -noout -text -in namu_wiki.crt}}}를 실행하면 앞서 입력한 CA 정보 등을 확인할 수 있다.] {{{#!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-----}}} === [[확장자]] === 가장 기본적인 확장자는 {{{*.pem}}}이지만, 워낙 base64가 다 비슷비슷하게 생겼고 라벨도 파일을 열지 않으면 읽기 어렵기 때문에 파일의 내용별로 확장자를 다르게 적는 관습이 있다. 문제는, 이 관습은 표준이 없이 그냥 제각각 편한 대로 쓴다는 점. [[TLS]]랑 [[OpenSSL]]을 공부하는 초심자라면 이 문서 저 문서 모두 같은 내용을 다루는데 확장자가 {{{*.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}}} 옵션을 넣지 않은 경우 기본적으로 바이너리로 출력되니 결국 파일을 열어보아야 알 수 있다. == 관련 문서 == * [[SSL]]/[[TLS]] * [[OpenSSL]] * [[GPG]]/[[GnuPG]] * [[RSA 암호화]] * [[디피-헬만 키 교환]] * [[Let's Encrypt]] * [[확장자]]