본문 바로가기
기타

[OpenSSL] 자체 CA 인증서 체인 구성하기 – OpenSSL 실전 가이드 🔐

by 띵앤띵 2025. 5. 26.
728x90

서브타이틀: 유니티 클라이언트와 rustls 서버 환경을 위한 신뢰 체인 구축

안녕하세요, 띵앤띵입니다! 😊
오늘은 OpenSSL을 이용해서 자체 서명된 Root CA, Intermediate CA, 그리고 최종 서버 인증서(Leaf)를 생성하고 체인으로 연결하는 과정을 소개할

게요.

 

 

💡 왜 이걸 딥다이빙하게 되었을까요?

최근 유니티 클라이언트를 사용하는 프로젝트에서 rustls 기반 HTTPS 통신을 구성하게 되었는데요. 이 rustls는 CA 인증 체인에 굉장히 엄격한 기준을 가지고 있더라고요. 예를 들면, 중간 CA가 없거나 잘못 서명된 경우 바로 통신을 차단해버려요. 😱

그래서 신뢰할 수 있는 인증서 체인을 스스로 만드는 방법을 제대로 정리해봤습니다. HTTPS나 내부 서비스의 보안을 강화하고 싶은 분들께 큰 도움이 될 거예요!

 


1. openssl.cnf 설정 (SAN 및 X.509v3 포함)

[ req ]
default_bits       = 2048
prompt             = no
default_md         = sha256
distinguished_name = req_distinguished_name
req_extensions     = req_ext

[ req_distinguished_name ]
C  = KR
ST = Seoul
L  = Seoul
O  = THINKANDTHING
CN = THINKANDTHING  # ← 도메인 주소를 THINKANDTHING으로 치환했어요

[ req_ext ]
subjectAltName = @alt_names

[ v3_ca ]
subjectAltName = @alt_names

[ alt_names ]
IP.1  = THINKANDTHING  # ← 실제 IP는 변수로 치환했습니다

2. 루트 인증서 생성 (Root CA)

1. 개인키 생성 (비밀키)
openssl genrsa -out root-ca.key.pem 4096
    
2. X.509 v3 self-signed 루트 인증서 서명
자신의 루트 비밀키(root-ca.key.pem) 로 자체 서명한 인증서 생성 → root-ca.cert.pem

openssl req -x509 -new -nodes \
  -key root-ca.key.pem \
  -sha256 -days 3650 \
  -out root-ca.cert.pem \
  -subj "/C=KR/ST=Seoul/O=THINKANDTHING/CN=MyRootCA"

 


3. 중간 인증서 생성 (Intermediate CA)

# 개인키 생성(비밀키)
openssl genrsa -out intermediate-ca.key.pem 4096

# 중간인증서 CSR 생성
openssl req -new -sha256 \
  -key intermediate-ca.key.pem \
  -out intermediate-ca.csr.pem \
  -subj "/C=KR/ST=Seoul/O=THINKANDTHING/CN=MyIntermediateCA"

# 중간인증서 서명 (Root 인증서를 KEY로 사용함)
openssl x509 -req -in intermediate-ca.csr.pem \
  -CA root-ca.cert.pem \
  -CAkey root-ca.key.pem \
  -CAcreateserial \
  -out intermediate-ca.cert.pem \
  -days 3650 -sha256 \
  -extfile openssl.cnf \
  -extensions v3_ca
  
# 중간인증서가 CA 인지 확인 명령어
openssl x509 -in intermediate-ca.cert.pem -noout -text | grep -A 3 "X509v3"

정상 예시)
X509v3 Basic Constraints: critical
    CA:TRUE
X509v3 Key Usage: critical
    Certificate Sign, CRL Sign
    
# rootCA 인증서로 intermediateCA 인증서가 제대로 발급되었는지 openssl 인증서 검증
openssl verify -CAfile root-ca.cert.pem intermediate-ca.cert.pem

# 정상 예시
intermediate-ca.cert.pem: OK

 


4. 서버 인증서 생성 (Leaf Certificate)

Leaf 인증서는 해당 인증서로 다른 인증서를 발급하지 못하는 인증서를 말합니다. 즉, CA 인증서가 아니라는 뜻입니다.

# 개인키 생성(비밀키)
openssl genrsa -out server.key.pem 2048

# CSR 생성 (SAN 포함, X.509 v3)
openssl req -new -key server.key.pem \
  -out server.csr.pem \
  -config openssl.cnf

# 서버 인증서 서명 (중간인증서의 KEY로 사용함)
openssl x509 -req -in server.csr.pem \
  -CA intermediate-ca.cert.pem \
  -CAkey intermediate-ca.key.pem \
  -CAcreateserial \
  -out server.cert.pem \
  -days 3650 -sha256 \
  -extensions req_ext \
  -extfile openssl.cnf

5. 인증서 체인 구성 (fullchain)

rustls 는 해당 구조를 신뢰합니다.

# 서버인증서(1)와 중간인증서(2)를 연결한 인증서 체인 구성 → fullchain.pem은 서버 구성 시 클라이언트에 인증 체인을 전달하는 용도로 사용. 반드시 순서 지켜야함 
cat server.cert.pem intermediate-ca.cert.pem > fullchain.pem

6. 인증서 검증

# 루트 인증서로 중간 체인을 참조하여 Leaf 인증서가 유효한지 확인
openssl verify -CAfile root-ca.cert.pem -untrusted fullchain.pem server.cert.pem

# 정상 예시
server.cert.pem: OK

## 이 조합은 "루트를 신뢰하면서, 중간 체인을 참조하여 leaf 인증서가 유효한지를 확인한다"
이 명령어는 다음의 의미를 가집니다:
  -CAfile root-ca.cert.pem → 신뢰의 시작점(루트 인증서)
  -untrusted fullchain.pem → 중간 인증서 제공 (이 파일에 중간 인증서가 포함되어 있어야 함)
  server.cert.pem → 검증 대상 (leaf 인증서)

7. PFX 파일로 변환

# 자바 게이트웨이에 적용할 thinkandthing.pfx 생성
openssl pkcs12 -export \
  -inkey server.key.pem \
  -in fullchain.pem \
  -out thinkandthing.pfx \
  -password pass:123456
  
# 버전 확인
openssl x509 -in thinkandthing.pfx -text -noout

# 정상 예시
Enter pass phrase for PKCS12 import pass phrase:
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            11:e7:33:b0:dd:93:c8:02:95:38:b6:24:77:ae:55:80:6d:2e:21:9d
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=KR, ST=Seoul, O=thinkandthing, CN=MyIntermediateCA
        Validity
            Not Before: May 22 03:07:36 2025 GMT
            Not After : Aug 25 03:07:36 2027 GMT
        Subject: C=KR, ST=Seoul, L=Seoul, O=thinkandthing CO., LTD., CN=thinkandthing.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    ~~
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                IP Address:192.168.0.178
            X509v3 Subject Key Identifier:
                2F:A9:C0:F2:D8:EB:83:23:59:94:CB:35:64:91:E0:A3:06:15:EA:4B
            X509v3 Authority Key Identifier:
                A9:57:3D:4D:AF:57:6E:54:97:FF:09:EC:00:B7:A2:BA:E4:6C:8B:04
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        ~~
No Trusted Uses.
No Rejected Uses.
Key Id: 5F:24:05:73:0B:70:36:7D:06:1D:41:FC:68:F3:FB:3C:D8:7A:35:2D


# SAN 필드 확인
openssl x509 -in thinkandthing.pfx -noout -text | grep -A1 "Subject Alternative Name"

# 정상 예시
Enter pass phrase for PKCS12 import pass phrase:
            X509v3 Subject Alternative Name:
                IP Address:192.168.0.178

 


8. 서버에 적용 후 연결 확인

'7. PFX 파일로 변환' 과정에서 생성된 thinkandthing.pfx 파일을 SSL 적용이 필요한 서버에 적용 후 테스트

예를들면, 스프링클라우드에서 게이트웨이가 될 수도 있고 WAS (Nginx, Tomcat) 에 적용될 수 있음

# Gateway 서버에 반영 후 로컬에서 서버 반영된 CA 검증
openssl s_client -connect THINKANDTHING:443 -CAfile root-ca.cert.pem

# 정상 예시
Connecting to 192.168.0.178
CONNECTED(00000003)
Can't use SSL_get_servername
depth=2 C=KR, ST=Seoul, O=THINKANDTHING, CN=MyRootCA
verify return:1
depth=1 C=KR, ST=Seoul, O=THINKANDTHING, CN=MyIntermediateCA
verify return:1
depth=0 C=KR, ST=Seoul, L=Seoul, O=THINKANDTHING CO., LTD., CN=THINKANDTHING.com
verify return:1
---
Certificate chain
 0 s:C=KR, ST=Seoul, L=Seoul, O=THINKANDTHING CO., LTD., CN=THINKANDTHING.com
   i:C=KR, ST=Seoul, O=THINKANDTHING, CN=MyIntermediateCA
   a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: May 22 03:07:36 2025 GMT; NotAfter: Aug 25 03:07:36 2027 GMT
 1 s:C=KR, ST=Seoul, O=THINKANDTHING, CN=MyIntermediateCA
   i:C=KR, ST=Seoul, O=THINKANDTHING, CN=MyRootCA
   a:PKEY: RSA, 4096 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: May 22 03:04:28 2025 GMT; NotAfter: May 21 03:04:28 2030 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIEijCCAnKgAwIBAgIUEeczsN2TyAKVOLYkd65VgG0uIZ0wDQYJKoZIhvcNAQEL
...
xhuaRn1ZZ76rs0KSqIA=
-----END CERTIFICATE-----
subject=C=KR, ST=Seoul, L=Seoul, O=THINKANDTHING CO., LTD., CN=THINKANDTHING.com
issuer=C=KR, ST=Seoul, O=THINKANDTHING, CN=MyIntermediateCA
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: rsa_pss_rsae_sha256
Peer Temp Key: X25519, 253 bits
---
SSL handshake has read 3129 bytes and written 1604 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Protocol: TLSv1.3
Server public key is 2048 bit
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: ....
    Session-ID-ctx:
    Resumption PSK: ....
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 86400 (seconds)
    TLS session ticket:
    0000 - ...
    0010 - ...

    Start Time: 1748235412
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---

 

 


9. 해당 인증서를 사용할 클라이언트에게 전달

클라이언트에게는 2단계 과정에서 생성했던 root-ca.cert.pem 해당 인증서만 전달해야함!


서버(Gateway) 측 인증서 파일 관리 원칙

파일 Git 업로드 외부 배포(클라이언트) 비고
server.key.pem ❌ 절대 금지 ❌ 절대 금지 개인키(비밀키) → 유출 시 인증서 무력화됨
server.cert.pem ❌ (가능한 제한) Leaf 인증서, 공개여도 되지만 독립적 의미 없음
intermediate-ca.cert.pem 중간 인증서, 서버에서 체인 전달용
fullchain.pem 서버 내부에서 TLS 체인용 사용
root-ca.cert.pem ❌ (선택사항) ✅ 유일하게 전달 가능 클라이언트 신뢰 시작점

 

 

✨ 마무리

이제 복잡해 보였던 인증서 발급과 체인 구성도 문제없죠?

특히 엄격한 보안 요구사항을 가진 환경(rustls, 유니티 클라이언트 등)에서는 필수적인 구성입니다.

궁금한 점은 댓글로 남겨주세요! 😊

반응형

'기타' 카테고리의 다른 글

[AWS] EC2 pem 키 SSH 접속시 bad permissions  (0) 2022.10.12
[MiniO] minio 사용법  (0) 2022.09.22
[GraphQL] 전체 스키마 조회 쿼리  (0) 2022.09.22

댓글