CSR 디코더
브라우저에서 디코딩되며 전송되지 않습니다. CSR은 개인 키와 맞닿아 있으므로 내용을 업로드하는 페이지에 붙여넣어서는 안 됩니다. 이 도구는 전송하지 않으며 개인 키를 요구하지도 않습니다.
입력한 내용은 저장, 기록, 공유되지 않습니다.
인증서 서명 요청(CSR)이나 X.509 인증서를 붙여넣으면 주체, 대체 이름, 키 알고리즘과 길이, 유효 기간, 확장, 지문을 읽을 수 있습니다. 모든 디코딩은 브라우저에서 이루어지며 내용은 전송되지 않습니다.
사용 방법
- BEGIN과 END 줄을 포함해 PEM 블록 전체를 붙여넣습니다.
- 주체, SAN, 키, 유효 기간, 확장 등 디코딩된 항목을 확인합니다.
- 지문을 복사해 서버나 콘솔이 표시하는 값과 대조합니다.
CSR에 담기는 것과 담기지 않는 것
인증서 서명 요청(CSR)에는 세 가지가 담깁니다. 인증서에 기재되기를 원하는 주체(subject), 키 쌍의 공개 키, 그리고 요청하는 확장 필드이며, 그중 가장 중요한 것은 주체 대체 이름(SAN)입니다. CSR은 개인 키로 서명되어, 인증 기관(CA)에 인증을 요청하는 키를 실제로 보유하고 있음을 증명합니다.
개인 키는 들어 있지 않으며, CSR의 어떤 내용으로도 개인 키를 알아낼 수 없습니다. 그래서 CSR은 키처럼 비밀 정보가 아니고, 도구에 붙여 넣어도 무리가 없습니다. 그렇다고 아무 정보도 없는 것은 아닙니다. 호스트 이름과 조직, 때로는 내부 명명 규칙까지 드러나므로, 사용하는 디코더가 이를 어딘가로 보내는지는 알아 두는 것이 좋습니다. 디코더 대부분은 인증서를 판매하는 회사가 운영하기 때문에 실제로 전송하는 곳도 많습니다.
CSR의 내용이 얼마나 적게 남는지도 알아 둘 만합니다. CA는 이름을 검증한 뒤 자체 일련번호, 유효 기간, 발급자, 그리고 어떤 확장을 넣을지에 대한 자체 판단으로 인증서를 발급합니다. 조직과 위치 필드는 입력한 값이 아니라 CA가 검증한 기록에서 가져옵니다. 실제로 이어지는 것은 공개 키와 이름 정도입니다.
Common Name은 낡았고, 중요한 것은 SAN
역사적인 이유로 주체의 Common Name(CN)은 사이트 이름을 나타내는 필드처럼 보이지만, 수년 전부터 그런 역할을 하지 않습니다. Chrome은 2017년에 CN을 인정하지 않기 시작했고 다른 브라우저도 뒤따랐으며, CA/Browser Forum은 인증서가 다루는 모든 이름을 SAN 확장에 넣도록 요구합니다. CN만 있고 SAN이 없는 인증서는 CN에 무엇이 쓰여 있든 현재의 모든 클라이언트에서 거부됩니다.
따라서 가장 먼저 읽어야 할 필드는 SAN 목록이며, 인증서가 서비스해야 하는 모든 호스트 이름이 들어 있어야 합니다. www 형태와 함께 도메인 자체로도 서비스한다면 그 이름도 필요합니다. 와일드카드는 정확히 한 단계의 레이블만 포함하므로 *.example.com은 www.example.com과 일치하지만 example.com이나 a.b.example.com과는 일치하지 않습니다.
CSR에서는 SAN을 놓치기 쉽고, 실제로 놓치는 디코더도 많습니다. 발급된 인증서에서 SAN은 최상위 확장이지만, 요청에서는 extensionRequest 속성 안에 두 단계 더 깊이 중첩되어 있습니다. 이 도구는 올바른 위치를 확인하므로, 여기서 목록이 비어 있다면 그 CSR에는 정말로 SAN이 없는 것입니다. 제출하기 전에 고쳐 두는 것이 좋습니다.
인증서 읽는 법
주체와 유효 기간 외에도 인증서가 무엇을 할 수 있는지 결정하는 확장이 몇 가지 있으며, 궁금한 답은 대개 여기에 있습니다.
Basic Constraints는 CA 인증서인지 여부를 나타냅니다. 리프 인증서에 CA:TRUE가 있다면 심각한 오발급이며, 서버 인증서라면 CA:FALSE여야 합니다. Key Usage와 Extended Key Usage는 키의 용도를 좁힙니다. TLS 서버는 serverAuth, 상호 TLS는 clientAuth입니다. 클라이언트가 기대하는 용도가 빠진 인증서는 나머지가 모두 올바르더라도 거부됩니다.
Authority Information Access에는 발급자 인증서의 URL(일부 클라이언트는 이를 이용해 불완전한 체인을 보완합니다)과 OCSP 응답자 URL이 들어 있습니다. 인증서 투명성(CT) 타임스탬프는 인증서가 공개 로그에 기록되었음을 보여 주며, 지금의 브라우저는 이를 필수로 요구합니다. Authority Key Identifier와 Subject Key Identifier는 체인 구성 시 실제로 대조하는 값으로, 발급자와 주체 문자열을 눈으로 비교하는 것보다 믿을 만합니다.
지문(fingerprint)은 SHA-256을 우선 사용하세요. SHA-1은 여러 콘솔과 피닝 설정에서 아직 표시하기 때문에 함께 보여 줄 뿐입니다. 둘 다 DER 인코딩을 기준으로 계산하므로 openssl 결과와 일치합니다.
체인 문제
가장 흔한 인증서 장애는 인증서 자체가 아니라 체인에서 생깁니다. 데스크톱 브라우저에서는 잘 되는데 curl, 모바일 앱, JVM 클라이언트에서는 실패하는 서버가 대표적입니다.
서버는 자신의 인증서와 함께 루트 직전까지의 모든 중간 인증서를 제시해야 합니다. 브라우저는 중간 인증서가 빠져 있어도 AIA URL에서 가져오거나 다른 사이트에서 캐시해 둔 것을 써서 문제를 덮는 경우가 많습니다. 그래서 Chrome으로 테스트하는 사람에게는 문제가 보이지 않고, 그 밖의 모든 클라이언트에서는 바로 실패합니다. 루트 인증서까지 보내는 것은 해롭지는 않지만 의미도 없습니다. 클라이언트는 이미 가지고 있거나 신뢰하지 않거나 둘 중 하나이기 때문입니다.
전통적인 TLS 체인에서는 순서가 중요합니다. 리프가 먼저, 그다음 각 발급자가 차례로 옵니다. 여기에 전체 체인을 붙여 넣으면 각 블록을 입력한 순서대로 따로 디코딩하므로, 빠지거나 위치가 잘못된 중간 인증서가 한눈에 드러납니다. 각 인증서의 발급자를 다음 인증서의 주체와 맞춰 보고, 중간 인증서의 만료일도 확인하세요. 중간 인증서도 만료되며, 만료된 중간 인증서는 완벽하게 유효한 리프 인증서까지 쓸 수 없게 만듭니다.
요청을 만드는 일, 그리고 그 뒤의 키
CSR은 언제나 키 쌍과 함께 만들어지며, 정작 중요한 것은 키 쪽입니다. 관행적으로 쓰는 명령은 둘을 한 번에 생성합니다. openssl req -new -newkey rsa:2048 -nodes -keyout example.key -out example.csr이죠. -nodes 플래그는 키를 암호화하지 않은 채로 남기는데, 이는 웹 서버가 패스프레이즈 입력 없이 기동하는 데 필요한 것이자, 그 파일을 서비스 계정만 읽을 수 있게 해야 하는 이유이기도 합니다.
호환성 측면에서 2048비트 RSA는 여전히 안전한 기본값입니다. P-256 곡선을 쓰는 ECDSA는 훨씬 작은 키와 빠른 핸드셰이크로 같은 수준의 보안을 제공하며 openssl ecparam -genkey -name prime256v1로 생성하고, 현행 브라우저는 모두 이를 받아들입니다. 다만 일부 오래된 미들박스와 레거시 Java 클라이언트는 그렇지 않은데, 지금 RSA를 고를 유일한 실질적 근거가 그것입니다.
subject alternative name은 대화형 프롬프트가 묻지 않기 때문에 사람들이 빠뜨리는 항목입니다. 설정 파일이나 -addext 플래그로 직접 넣어야 하며, 예를 들어 -addext "subjectAltName=DNS:example.com,DNS:www.example.com"처럼 씁니다. 이것이 빠진 요청은 거부되거나, 더 나쁘게는 어떤 브라우저도 받아들이지 않는 인증서로 발급되고 맙니다.
자동 발급이 바꿔 놓은 작업 흐름
CSR을 만들고, 벤더 포털에 붙여 넣고, 기다리고, 인증서를 내려받아 설치하고, 내년에 다시 반복하는 수작업 주기는 이제 예외적인 방식이 되었습니다. Let's Encrypt가 2016년에 도입한 ACME 프로토콜은 이 전 과정을 자동화합니다. 클라이언트가 도메인 통제권을 증명하고 CSR을 제출하면 인증서를 받는데, 보통 몇 초 만에, 사람 손을 전혀 거치지 않고 끝납니다.
도메인 통제권은 세 가지 방식 중 하나로 증명합니다. HTTP-01은 well-known 경로에 토큰을 제공하는 방식으로, 80번 포트에 접근할 수 있어야 합니다. DNS-01은 TXT 레코드를 게시하는 방식으로, 와일드카드를 발급할 수 있는 유일한 방법이자 공개 웹 서버가 없는 호스트에서 쓸 수 있는 유일한 방법입니다. TLS-ALPN-01은 443번 포트에서 특별한 핸드셰이크에 응답하는 방식으로, 그 포트만 열려 있는 환경에서 쓸모가 있습니다.
이렇게 발급된 인증서는 설계상 90일 동안만 유효합니다. 수명이 짧으면 키가 유출됐을 때 피해가 제한되고, 갱신 절차가 기억에 의존하는 대신 자동화될 수밖에 없다는 논리입니다. 클라이언트는 대체로 수명의 3분의 2 지점에서 갱신하므로, 실패하더라도 무언가가 만료되기 전에 알아챌 시간이 남습니다.
검증 수준, 와일드카드, 그리고 인증서가 증명하는 것
도메인 검증이 증명하는 것은 인증서를 요청한 사람이 그 순간 해당 도메인을 통제하고 있었다는 사실뿐입니다. 조직 검증과 확장 검증은 그 뒤에 있는 법인에 대한 확인을 더하고, 그 내용이 발급된 인증서의 주체 필드에 담깁니다. 브라우저는 몇 년 전부터 EV에 특별한 UI를 부여하지 않으므로, 오늘날 실질적인 차이는 방문자가 볼 수 있는 무언가라기보다 대체로 계약과 절차의 문제입니다.
와일드카드는 정확히 한 레이블만 포함합니다. *.example.com은 www와 api에는 맞지만 example.com 자체나 a.b.example.com에는 맞지 않습니다. 대부분의 와일드카드 인증서가 맨 도메인을 SAN으로 추가해 두는 이유가 바로 이것입니다. 또한 와일드카드는 위험을 한곳에 모읍니다. 키 하나가 털리면 그 이름 아래 모든 호스트가 영향을 받고, DNS 통제권 없이는 발급 자체가 불가능합니다.
이 가운데 어느 것도 그 사이트가 믿을 만한지에 대해서는 아무 말도 해 주지 않습니다. 인증서는 연결이 암호화되어 있고 이름이 일치한다는 뜻이지, 운영자가 정직하다는 뜻이었던 적은 한 번도 없습니다. 피싱 사이트가 유효한 인증서를 갖고 있는 것은 당연한 일이며, 그래서 브라우저 UI도 그 반대를 암시하는 표현을 없앴습니다.
만료는 인증서의 문제가 아니라 운영의 문제
인증서 만료로 생긴 장애는 하나의 장르라 할 만큼 흔합니다. 결제 네트워크와 이동통신사, 클라우드 콘솔까지 이 때문에 멈춘 적이 있습니다. 인증서는 설계된 대로 정확히 동작했을 뿐이고, 실패한 것은 그 주변의 절차였습니다.
모니터링은 실패가 아니라 남은 일수를 기준으로 경보를 울려야 하며, 대응할 시간이 충분할 만큼 여유 있는 임계값을 잡아야 합니다. 90일짜리 인증서라면 2주, 1년짜리라면 한 달입니다. 파일이 아니라 엔드포인트를 감시하십시오. 중요한 것은 서버가 실제로 제시하는 인증서이기 때문입니다. 중간 인증서도 함께 확인하십시오. 중간 인증서 역시 만료되며, 만료된 중간 인증서는 아무 문제 없는 리프 인증서까지 못 쓰게 만듭니다.
인증서 투명성 로그는 공짜 자산 목록이 되어 줍니다. 공개적으로 신뢰되는 모든 인증서가 로그에 남으므로, 자기 도메인을 검색하면 어떤 인증서가 존재하는지 드러납니다. 조직 안의 다른 누군가가 발급해 놓고 알려 주지 않은 인증서까지 말이죠.
무언가 안 될 때 체인 읽기
가장 쓸모 있는 단일 명령은 openssl s_client -connect host:443 -servername host입니다. 설정 파일에 무엇이 적혀 있는지가 아니라 서버가 실제로 무엇을 제시하는지를 보여 주기 때문입니다. 출력되는 체인은 리프 인증서로 시작해 발급자가 차례로 이어져야 하고, 각 인증서의 issuer 줄은 다음 인증서의 subject 줄과 일치해야 합니다.
중간 인증서 누락은 전형적인 실패이며 데스크톱 브라우저에서는 보이지 않습니다. 브라우저가 빠진 인증서를 직접 가져오거나 다른 사이트에서 캐시해 두었기 때문입니다. curl과 모바일 애플리케이션, JVM 클라이언트는 그렇게 하지 않습니다. 그래서 증상은 테스트한 사람에게는 잘 되는데 실제 트래픽의 상당 부분에서는 실패하는 사이트로 나타납니다.
체인이 멀쩡해 보이는데도 연결이 실패한다면, 다음으로 볼 것은 키 용도(key usage) 확장, 클라이언트가 그 루트를 애초에 신뢰하는지 여부, 그리고 클라이언트의 시계입니다. 날짜가 틀어진 기기는 아무 문제 없는 인증서를 아직 유효하지 않다며 거부합니다.
자주 묻는 질문
- CSR을 웹 페이지에 붙여넣어도 안전한가요?
- 이 페이지라면 안전합니다. 전송되는 것이 없고 디코딩이 브라우저에서 이루어지며 텍스트가 기기를 벗어나지 않기 때문입니다. 다만 일반적으로는 주의해야 합니다. 대부분의 인증서 디코더는 인증서를 파는 회사가 운영하며, 그중 몇몇은 CSR을 자사 서버로 보냅니다. CSR이 개인 키만큼 비밀은 아니지만 인프라 구성을 설명하는 문서이므로, 자신이 어떤 종류의 도구를 쓰는지는 알고 있어야 합니다.
- 인증서가 유효하거나 신뢰할 수 있는지 확인해 주나요?
- 아니며, 그런 척도 하지 않습니다. 이 도구는 문서가 주장하는 내용만 읽습니다. 서명을 검증하지 않고, 루트까지의 체인을 만들지 않으며, CA나 OCSP 응답자에 연결하지도 않습니다. 이 목적으로 브라우저가 정직하게 참조할 수 있는 신뢰 저장소가 없으므로, 초록색 "신뢰됨" 배지를 보여 주는 도구는 오해를 부릅니다. 표시되는 유효 기간은 인증서에 적힌 값을 사용자의 시계와 비교한 결과입니다.
- 무엇을 붙여넣을 수 있나요?
- CSR, 인증서, 또는 체인 전체 모두 가능합니다. PEM 블록마다 따로 디코딩해 순서대로 보여 주므로 중간 인증서가 빠졌는지 순서가 틀렸는지 가장 빠르게 확인할 수 있습니다. BEGIN과 END 줄이 없는 순수 base64도 됩니다. JSON 필드나 쿠버네티스 시크릿에서 복사한 인증서가 그런 모양이기 때문입니다. 개인 키는 디코딩하지 않고 거부합니다.
- 제 CSR에 대체 이름이 안 보이는 이유는 무엇인가요?
- 정말 없거나, 이전에 쓰던 디코더가 찾지 못했거나 둘 중 하나입니다. CSR에서 SAN은 최상위에 있지 않고 extensionRequest 속성 안, 완성된 인증서보다 두 단계 더 깊은 곳에 들어 있습니다. 이 도구는 그곳을 봅니다. 여기서도 보이지 않는다면 그 CSR은 실제로 SAN 없이 생성된 것이고, 제출 전에 고칠 가치가 있습니다. 최신 브라우저는 공통 이름을 아예 무시합니다.
- 어떤 지문을 비교해야 하나요?
- 무언가가 특별히 SHA-1을 요구하지 않는 한 SHA-256입니다. 둘 다 보여 주는 이유는 여러 콘솔과 피닝 설정, 벤더 대시보드가 여전히 SHA-1을 출력하기 때문이며, 눈앞의 값과 맞춰볼 수 있어야 합니다. 둘 다 DER 인코딩에 대해 계산하므로 openssl x509 -fingerprint의 출력과 정확히 일치합니다.
- 날짜가 보기보다 중요한 이유는 무엇인가요?
- 인증서에는 두 가지 시간 표현이 있고, 2050년 이전 날짜는 두 자리 연도로 저장됩니다. RFC 5280은 50에서 99를 19xx로, 00에서 49를 20xx로 규정하므로 이 경계를 잘못 구현한 디코더는 2049년까지 유효한 인증서를 1949년에 만료된 것으로 표시합니다. 이 도구는 RFC를 따르며 그것을 고정하는 테스트가 있습니다.
마지막 업데이트:
진행 단계, 예상 결과, 실제 결과를 적어 주세요. 비밀 정보와 개인 정보는 삭제해 주세요.
