일반적으로 https 통신을 하기 위해서 인증서를 구매하거나 ACME(Automatic Certificate Management Environment) 중 하나인 "Let's Encrypt" 통해 많이들 갱신한다.
이때 발생하는 여러가지 인증서의 종류를 알고 이를 검증, 시스템에 적용하는 방법을 확인한다.
HTTPS 인증서의 종류
- ROOT Certificate : 정식 인증기관에서 발행하는 인증서로 시스템에 저장되어 있다.
- Intermediate Certificate : ROOT Certificate와 Site Certificate를 연결해주는 인증서로 Chain Certificate라고도 불린다.
- End-entify Certificate : 업체에서 구매하여 사이트에 적용하는 서버용 인증서
인증서 확인
일반적으로 알려진 "www.naver.com"와 "www.ridi.com" 사이트를 대상으로 인증서를 확인하고 어떻게 운영되는지 확인해본다.
1. Root Certificate를 서버에서 제공하지 않는 케이스
safari로 해당 사이트를 열어서 인증서를 확인해보면 아래와 같이 정보가 확인된다.

ROOT 인증서 고유 이름(CN)은 "DigiCert Global Root CA"
Intermediate(chain) 인증서 고유 이름(CN)은 "DigiCert TLS Hybrid ECC SHA384 2020 CA1"
마지막 사이트를 인증해주는 인증서 고유 이름(CN)은 "*.naver.com"이다.
정말로 위 3가지 인증서를 모두 내려주는지 아래 명령어를 통해서 확인해본다.
$ openssl s_client -connect www.naver.com:443
결과 값은 아래와 같다.

결과 부분에서 해석 시, 중요하게 확인해봐야할 부분이 "depth" 부분과 "Certificate chain" 부분이다.
depth는 인증서가 정확하게 Verification할 수 있는 루트를 알려준다.
Certificate chain는 서버에서 내려주는 인증서를 알려준다.
우리가 관심 깊게 봐야할 부분이 아래 "depth" 부분과 "Certificate chain" 부분이다.
depth=2 C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA
verify return:1
depth=1 C=US, O=DigiCert Inc, CN=DigiCert TLS Hybrid ECC SHA384 2020 CA1
verify return:1
depth=0 C=KR, ST=Gyeonggi-do, L=Seongnam-si, O=NAVER Corp., CN=*.naver.com
verify return:1
---
Certificate chain
0 s:C=KR, ST=Gyeonggi-do, L=Seongnam-si, O=NAVER Corp., CN=*.naver.com
i:C=US, O=DigiCert Inc, CN=DigiCert TLS Hybrid ECC SHA384 2020 CA1
a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA384
v:NotBefore: Mar 5 00:00:00 2025 GMT; NotAfter: Mar 17 23:59:59 2026 GMT
1 s:C=US, O=DigiCert Inc, CN=DigiCert TLS Hybrid ECC SHA384 2020 CA1
i:C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA
a:PKEY: id-ecPublicKey, 384 (bit); sigalg: RSA-SHA384
v:NotBefore: Apr 14 00:00:00 2021 GMT; NotAfter: Apr 13 23:59:59 2031 GMT
---
depth=0
에 대응되는 Certificate chain
은 "0"
이고 각각 대응되는데, 문제는 depth=2
에 대응되는 "DigiCert Global Root CA"
는 서버에서 인증서에 포함하지 않고 내려주고 있다.
이는 시스템에 저장된 Root 인증서를 참조한다.
만약 Ubuntu라면 아래 위치한 파일이다. (Root Certificate는 "/etc/ssl/certs/
"에 위치하고 있고 여러 곳에서 사용되고 있다.)
/etc/ssl/certs/DigiCert_Global_Root_CA.pem
Root certificate가 생략되는 이유는 아래 rfc5246의 Server Certificate의 certificated_list 부분을 참고하면 좋다.
- https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.2
certificate_list
This is a sequence (chain) of certificates. The sender's
certificate MUST come first in the list. Each following
certificate MUST directly certify the one preceding it. Because
certificate validation requires that root keys be distributed
independently, the self-signed certificate that specifies the root
certificate authority MAY be omitted from the chain, under the
assumption that the remote end must already possess it in order to
validate it in any case.
2. Root Certificate를 서버에서 제공하는 케이스
아래 명령어로 www.ridi.com
사이트를 검증해본다.
$ openssl s_client -connect www.ridi.com:443

위에서 보다 싶에 depth가 0, 1, 2
인데 Certificate chain을 살펴보면 0, 1, 2
로 3가지 모두 제공하고 있다.
위와 같이 Root Certificate를 Server에서 제공해주는 것이 좋을지? 아니면 Browser에서 신뢰하도록 할지는
stackoverflow나 reddit에서도 정확하게 결론이 나지 않은 부분이다.문제가 되는 케이스가 있긴한데, 아주 희귀한 케이스가 있다. 정보 보안적인 측면 & 사업적 측면에서 해당 클라이언트를
제외할지를 고민해야되는 케이스라.... 결론이 쉽지는 않다.
인증서 Verification
아래와 같이 현재 사이트에 적용한 인증서를 확인하면 아래와 같다.

"Let's Encrypt"에서 발급 받은 인증서인데, End-entify Certicate와 Intermediate Certificate의 결합된 상태의 인증서로 갱신하도록
해놓은 형태이다. 이를 분리해서 End-entify Certicate와 Intermediate Certificate로 저장한다.
보통 Full Bundle 인증서의 경우 아래와 같은 형식으로 저장해서 사용하면 된다.
End-entify Certificate | -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- |
Intermediate Certificate | -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- |
Root Certificate (생략 가능) | -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- |
현재 사용하고 있는 fullchain.pem
파일을 분리해서 intermediate-encicle-com-end-entity.pem
와 star-encicle-com-end-entity.pem
로
분리해서 각각의 인증서를 단계별로 검증해본다.
인증서를 검증할 때는 openssl verify
명령어를 많이 사용한다.
Root Certificate가 포함되지 않는 형태이므로 시스템에서 "ISRG Root X1" 인증서를 찾아보면 아래와 같이 확인이 가능하다.
$ ls /etc/ssl/certs/* |grep ISRG
/etc/ssl/certs/ISRG_Root_X1.pem
/etc/ssl/certs/ISRG_Root_X2.pem
$
/etc/ssl/certs/ISRG_Root_X1.pem
Root 인증로 확인된다.
아래와 같은 명령어로 Root와 intermediate Certificate를 검증해본다.
아래와 같이 OK
결과가 출력되면 문제가 없는 것이다.
$ openssl verify -no-CAfile -no-CApath -partial_chain -trusted /etc/ssl/certs/ISRG_Root_X1.pem intermediate-encicle-com-end-entity.pem
intermediate-encicle-com-end-entity.pem: OK
$
-no-CAfile -no-CApath
옵션은 시스템에 설정된 인증서를 인용하기 않게 하기 위한 옵션이다.
만약 다른 Root 인증서로 검증하면 아래와 같이 error가 발생한다.
$ openssl verify -no-CAfile -no-CApath -partial_chain -trusted /etc/ssl/certs/DigiCert_Global_Root_CA.pem intermediate-encicle-com-end-entity.pem
C = US, O = Let's Encrypt, CN = E5
error 20 at 0 depth lookup: unable to get local issuer certificate
error intermediate-encicle-com-end-entity.pem: verification failed
$
인증서가 형식적으로 정확한 것으로 확인되었다.
그러나 실제 인증서를 사용하다보면 OCSP라는 과정이 포함되어 있다.
보통 폐쇠망에서 공인 인증서를 사용하다보면, 인증서 오류가 발생될 가능성이 높은데, 이는 인증서를 외부 시스템에 한번 더 검증하는Online Certificate Status Protocol
체크가 포함되어 있기 때문이다.
전체적인 인증서가 올바른지 테스트를 하기 위해서는 일단 ocsp uri를 인증서에서 아래와 같이 추출한다. (인증서가 x509 형식인 경우)
$ openssl x509 -noout -ocsp_uri -in star-encicle-com-end-entity.pem
http://e5.o.lencr.org
가지고 있는 인증서를 통해서 최종적으로 oscp 까지 정상적인지 추출된 ocsp uri와 인증서를 가지고 검증한다.
$ openssl ocsp -issuer intermediate-encicle-com-end-entity.pem -cert star-encicle-com-end-entity.pem -text -url http://e5.o.lencr.org

"Response verify OK" 문자열을 확인했으면 아주 정상적인 인증서라 확인할 수 있다.
인증서 내용 확인 (x509 기반 인증서)
인증서 내용을 확인할 수 있는 명령어로는 아래와 같이 인증서를 text로 출력해본다.
$ openssl x509 -text -noout -in star-encicle-com-end-entity.pem

필요에 따라서는 asn1parse로 확인해볼 수도 있다.
$ openssl asn1parse -in star-encicle-com-end-entity.pem
0:d=0 hl=4 l= 891 cons: SEQUENCE
4:d=1 hl=4 l= 770 cons: SEQUENCE
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :02
13:d=2 hl=2 l= 18 prim: INTEGER :03015BAA831B479ADC8XXXXXXXXXX
33:d=2 hl=2 l= 10 cons: SEQUENCE
35:d=3 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA384
45:d=2 hl=2 l= 50 cons: SEQUENCE
47:d=3 hl=2 l= 11 cons: SET
49:d=4 hl=2 l= 9 cons: SEQUENCE
51:d=5 hl=2 l= 3 prim: OBJECT :countryName
56:d=5 hl=2 l= 2 prim: PRINTABLESTRING :US
60:d=3 hl=2 l= 22 cons: SET
62:d=4 hl=2 l= 20 cons: SEQUENCE
64:d=5 hl=2 l= 3 prim: OBJECT :organizationName
69:d=5 hl=2 l= 13 prim: PRINTABLESTRING :Let's Encrypt
84:d=3 hl=2 l= 11 cons: SET
86:d=4 hl=2 l= 9 cons: SEQUENCE
88:d=5 hl=2 l= 3 prim: OBJECT :commonName
93:d=5 hl=2 l= 2 prim: PRINTABLESTRING :E5
97:d=2 hl=2 l= 30 cons: SEQUENCE
99:d=3 hl=2 l= 13 prim: UTCTIME :250223140151Z
114:d=3 hl=2 l= 13 prim: UTCTIME :250524140150Z
129:d=2 hl=2 l= 24 cons: SEQUENCE
131:d=3 hl=2 l= 22 cons: SET
133:d=4 hl=2 l= 20 cons: SEQUENCE
135:d=5 hl=2 l= 3 prim: OBJECT :commonName
140:d=5 hl=2 l= 13 prim: UTF8STRING :*.encicle.com
155:d=2 hl=2 l= 89 cons: SEQUENCE
157:d=3 hl=2 l= 19 cons: SEQUENCE
159:d=4 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
168:d=4 hl=2 l= 8 prim: OBJECT :prime256v1
178:d=3 hl=2 l= 66 prim: BIT STRING
...
인증서를 서비스에 적용하기 전 자세하게 검증하는 방법이라던가, OCSP의 검증 등을 통해 다양하게 사용해볼 수 있다.