개요
이 글에서는 OpenSSL을 사용하여 실제로 인증서를 생성하고 관리하는 방법을 다룹니다.
사전 지식: x.509 인증서와 PKI - 구조와 원리를 먼저 읽으시면 좋습니다.
OpenSSL 설치 확인
설치 여부 확인
openssl version
# OpenSSL 3.0.11 19 Sep 2023 (Library: OpenSSL 3.0.11 19 Sep 2023)설치
# macOS
brew install openssl
# Ubuntu/Debian
sudo apt-get install openssl
# CentOS/RHEL
sudo yum install opensslPrivate Key 생성
RSA 방식
# 2048-bit RSA 개인키 생성
openssl genrsa -out server.key 2048
# 4096-bit (더 안전, 성능 느림)
openssl genrsa -out server.key 4096
# 비밀번호로 보호된 개인키
openssl genrsa -aes256 -out server.key 2048
# Enter pass phrase: ****ECDSA 방식 (타원곡선)
# secp256r1 (NIST P-256) 곡선 사용
openssl ecparam -genkey -name prime256v1 -out server.key
# 사용 가능한 곡선 목록 확인
openssl ecparam -list_curvesRSA vs ECDSA:
| 구분 | RSA 2048-bit | ECDSA P-256 |
|---|---|---|
| 보안 수준 | 112-bit | 128-bit |
| 키 크기 | 2048-bit | 256-bit |
| 서명 속도 | 느림 | 빠름 |
| 검증 속도 | 빠름 | 느림 |
| 호환성 | 매우 높음 | 중간 |
개인키 확인
# RSA 개인키 정보 출력
openssl rsa -in server.key -text -noout
# ECDSA 개인키 정보 출력
openssl ec -in server.key -text -nooutCSR (Certificate Signing Request) 생성
CSR은 CA에게 인증서 발급을 요청할 때 사용합니다.
대화형 생성
openssl req -new -key server.key -out server.csr
# 입력 항목:
# Country Name (2 letter code) [XX]:KR
# State or Province Name (full name) []:Seoul
# Locality Name (eg, city) []:Gangnam
# Organization Name (eg, company) []:Example Corp
# Organizational Unit Name (eg, section) []:IT Department
# Common Name (eg, YOUR name) []:example.com
# Email Address []:admin@example.com설정 파일로 생성
csr.conf 파일 생성:
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[dn]
C = KR
ST = Seoul
L = Gangnam
O = Example Corp
OU = IT Department
CN = example.com
emailAddress = admin@example.com
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = api.example.comCSR 생성:
openssl req -new -key server.key -out server.csr -config csr.confCSR 확인
openssl req -in server.csr -text -noout주요 확인 사항:
- Subject: CN, O, C 등이 올바른지
- Public Key: 알고리즘과 키 크기
- Subject Alternative Name: 다중 도메인 확인Self-Signed 인증서 생성
개발/테스트 환경에서 사용하는 자체 서명 인증서입니다.
기본 방법
# 개인키 + 자체 서명 인증서 한 번에 생성 (365일 유효)
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout selfsigned.key \
-out selfsigned.crt \
-days 365 \
-subj "/C=KR/ST=Seoul/O=Example/CN=localhost"기존 개인키로 생성
# 이미 있는 개인키 사용
openssl req -x509 -new -key server.key \
-out selfsigned.crt -days 365 \
-subj "/C=KR/ST=Seoul/O=Example/CN=localhost"SAN 포함 (다중 도메인)
selfsigned.conf:
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_ca
[dn]
CN = localhost
[v3_ca]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[alt_names]
DNS.1 = localhost
DNS.2 = *.localhost
DNS.3 = 127.0.0.1
IP.1 = 127.0.0.1
IP.2 = ::1생성:
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout selfsigned.key \
-out selfsigned.crt \
-days 365 \
-config selfsigned.confRoot CA 만들기
실제 Certificate Chain을 구성해봅니다.
1. Root CA 개인키 생성
# 4096-bit (Root CA는 더 강력한 키 사용)
openssl genrsa -aes256 -out rootCA.key 4096
# Enter pass phrase: **** (안전하게 보관!)2. Root CA 인증서 생성
rootCA.conf:
[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_ca
[dn]
C = KR
O = Example Root CA
CN = Example Root CA
[v3_ca]
basicConstraints = critical, CA:TRUE
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
subjectKeyIdentifier = hash생성 (10년 유효):
openssl req -x509 -new -key rootCA.key \
-out rootCA.crt -days 3650 \
-config rootCA.conf
# Enter pass phrase for rootCA.key: ****3. Root CA 확인
openssl x509 -in rootCA.crt -text -noout확인 사항:
- Issuer == Subject (자체 서명)
- X509v3 Basic Constraints: CA:TRUE
- X509v3 Key Usage: Certificate Sign, CRL Sign
- Validity: 10년Intermediate CA 만들기
1. Intermediate CA 개인키 생성
openssl genrsa -aes256 -out intermediateCA.key 40962. Intermediate CA CSR 생성
intermediateCA.conf:
[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_intermediate_ca
[dn]
C = KR
O = Example Intermediate CA
CN = Example Intermediate CA
[v3_intermediate_ca]
basicConstraints = critical, CA:TRUE, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign생성:
openssl req -new -key intermediateCA.key \
-out intermediateCA.csr \
-config intermediateCA.conf3. Root CA로 Intermediate CA 서명
openssl x509 -req -in intermediateCA.csr \
-CA rootCA.crt -CAkey rootCA.key \
-CAcreateserial \
-out intermediateCA.crt \
-days 1825 \
-sha256 \
-extfile intermediateCA.conf \
-extensions v3_intermediate_ca
# Enter pass phrase for rootCA.key: ****4. Intermediate CA 확인
openssl x509 -in intermediateCA.crt -text -noout확인 사항:
- Issuer: CN=Example Root CA (Root CA가 서명)
- Subject: CN=Example Intermediate CA
- X509v3 Basic Constraints: CA:TRUE, pathlen:0
→ pathlen:0 = 이 아래 더 이상 하위 CA 불가End-entity 인증서 발급
실제 서버가 사용할 인증서를 Intermediate CA로 발급합니다.
1. 서버 개인키 생성
openssl genrsa -out server.key 20482. 서버 CSR 생성
server.conf:
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[dn]
C = KR
O = Example Corp
CN = example.com
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = api.example.com생성:
openssl req -new -key server.key \
-out server.csr \
-config server.conf3. Intermediate CA로 서명
server_cert.conf:
[v3_end]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = api.example.com서명:
openssl x509 -req -in server.csr \
-CA intermediateCA.crt -CAkey intermediateCA.key \
-CAcreateserial \
-out server.crt \
-days 365 \
-sha256 \
-extfile server_cert.conf \
-extensions v3_end
# Enter pass phrase for intermediateCA.key: ****Certificate Chain 구성
Chain 파일 생성
서버에서 사용할 전체 체인 파일을 만듭니다:
# 순서 중요: 서버 인증서 → Intermediate CA → Root CA
cat server.crt intermediateCA.crt rootCA.crt > fullchain.crt또는 Intermediate만 포함 (일반적):
# Root CA는 클라이언트가 이미 가지고 있다고 가정
cat server.crt intermediateCA.crt > chain.crtNginx 설정 예시
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/chain.crt; # 전체 체인
ssl_certificate_key /path/to/server.key; # 개인키
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
}Apache 설정 예시
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /path/to/server.crt
SSLCertificateKeyFile /path/to/server.key
SSLCertificateChainFile /path/to/intermediateCA.crt
</VirtualHost>인증서 검증
형식 확인
# PEM 형식 확인
openssl x509 -in server.crt -text -noout
# 시작/종료 날짜만 확인
openssl x509 -in server.crt -noout -dates
# notBefore=Jan 1 00:00:00 2024 GMT
# notAfter=Jan 1 00:00:00 2025 GMT
# Subject와 Issuer 확인
openssl x509 -in server.crt -noout -subject -issuer개인키와 인증서 일치 확인
# 개인키의 modulus (MD5 해시)
openssl rsa -noout -modulus -in server.key | openssl md5
# (stdin)= a1b2c3d4...
# 인증서의 modulus
openssl x509 -noout -modulus -in server.crt | openssl md5
# (stdin)= a1b2c3d4...
# 두 값이 같으면 → 개인키와 인증서가 쌍임CSR과 인증서 일치 확인
# CSR의 modulus
openssl req -noout -modulus -in server.csr | openssl md5
# 인증서의 modulus
openssl x509 -noout -modulus -in server.crt | openssl md5
# 같으면 → CSR로 만든 인증서가 맞음Certificate Chain 검증
# Intermediate CA가 Root CA로 서명되었는지 확인
openssl verify -CAfile rootCA.crt intermediateCA.crt
# intermediateCA.crt: OK
# 서버 인증서가 전체 체인으로 유효한지 확인
openssl verify -CAfile rootCA.crt -untrusted intermediateCA.crt server.crt
# server.crt: OK옵션 설명:
-CAfile: 신뢰하는 Root CA-untrusted: 중간 인증서 체인 (신뢰는 안 하지만 검증에 사용)
SSL/TLS 연결 테스트
# 특정 서버의 인증서 확인
openssl s_client -connect example.com:443 -showcerts
# SNI (Server Name Indication) 지정
openssl s_client -connect example.com:443 -servername example.com
# 특정 Root CA로 검증
openssl s_client -connect example.com:443 -CAfile rootCA.crt출력에서 확인 사항:
- Verify return code: 0 (ok) → 검증 성공
- Verify return code: 20 (unable to get local issuer certificate) → Root CA 없음
- Verify return code: 10 (certificate has expired) → 만료됨인증서 포맷 변환
PEM과 DER
# PEM → DER (바이너리)
openssl x509 -in server.crt -outform DER -out server.der
# DER → PEM (텍스트)
openssl x509 -in server.der -inform DER -out server.crt포맷 설명:
- PEM: Base64 인코딩,
-----BEGIN CERTIFICATE-----헤더 - DER: 바이너리 형식, Java 등에서 사용
PKCS#12 (PFX)
개인키 + 인증서 + 체인을 하나의 파일로:
# PEM → PKCS#12
openssl pkcs12 -export \
-in server.crt \
-inkey server.key \
-certfile intermediateCA.crt \
-out server.p12 \
-name "My Server Cert"
# Enter Export Password: ****
# PKCS#12 → PEM
openssl pkcs12 -in server.p12 -out server.pem -nodes
# Enter Import Password: ****용도: Windows IIS, Java Keystore 등에서 사용
인증서 정보 추출
Subject Alternative Names
openssl x509 -in server.crt -text -noout | grep -A 1 "Subject Alternative Name"
# X509v3 Subject Alternative Name:
# DNS:example.com, DNS:www.example.com공개키 추출
# 인증서에서 공개키만 추출
openssl x509 -in server.crt -pubkey -noout > public.key
# 개인키에서 공개키 추출
openssl rsa -in server.key -pubout -out public.keyFingerprint (지문)
# SHA-256 fingerprint
openssl x509 -in server.crt -noout -fingerprint -sha256
# SHA256 Fingerprint=A1:B2:C3:D4:...
# SHA-1 fingerprint
openssl x509 -in server.crt -noout -fingerprint -sha1용도: 인증서 고유 식별, Pin 검증 등
인증서 폐기 (CRL)
CRL 생성
crl.conf:
[ca]
default_ca = CA_default
[CA_default]
database = index.txt
crlnumber = crlnumber
default_crl_days = 30
default_md = sha256필요 파일 생성:
# 폐기 인증서 데이터베이스
touch index.txt
# CRL 일련번호
echo "1000" > crlnumber인증서 폐기:
# 인증서 폐기 등록
openssl ca -revoke server.crt \
-keyfile intermediateCA.key \
-cert intermediateCA.crt \
-config crl.conf
# CRL 생성
openssl ca -gencrl \
-keyfile intermediateCA.key \
-cert intermediateCA.crt \
-out crl.pem \
-config crl.confCRL 확인
openssl crl -in crl.pem -text -nooutCRL로 검증
openssl verify -CAfile rootCA.crt \
-untrusted intermediateCA.crt \
-crl_check -CRLfile crl.pem \
server.crt실전 시나리오
시나리오 1: 로컬 개발 환경 HTTPS
# 1. 개인키 생성
openssl genrsa -out local.key 2048
# 2. 자체 서명 인증서 (localhost + 127.0.0.1)
cat > local.conf <<EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_req
[dn]
CN = localhost
[v3_req]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
[alt_names]
DNS.1 = localhost
DNS.2 = *.localhost
IP.1 = 127.0.0.1
IP.2 = ::1
EOF
openssl req -x509 -new -key local.key \
-out local.crt -days 365 -config local.conf
# 3. macOS 키체인에 등록 (신뢰 설정)
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain local.crt시나리오 2: 내부망 Private CA 구축
# 1. Root CA 생성
openssl genrsa -aes256 -out internal-rootCA.key 4096
openssl req -x509 -new -key internal-rootCA.key \
-out internal-rootCA.crt -days 7300 \
-subj "/C=KR/O=Internal Root CA/CN=Internal Root CA"
# 2. Intermediate CA 생성
openssl genrsa -aes256 -out internal-intermediateCA.key 4096
openssl req -new -key internal-intermediateCA.key \
-out internal-intermediateCA.csr \
-subj "/C=KR/O=Internal Intermediate CA/CN=Internal Intermediate CA"
openssl x509 -req -in internal-intermediateCA.csr \
-CA internal-rootCA.crt -CAkey internal-rootCA.key \
-CAcreateserial -out internal-intermediateCA.crt \
-days 3650 -sha256
# 3. 서버 인증서 발급 (자동화 스크립트)
cat > issue-cert.sh <<'EOF'
#!/bin/bash
DOMAIN=$1
openssl genrsa -out ${DOMAIN}.key 2048
openssl req -new -key ${DOMAIN}.key -out ${DOMAIN}.csr \
-subj "/C=KR/O=Internal/CN=${DOMAIN}"
openssl x509 -req -in ${DOMAIN}.csr \
-CA internal-intermediateCA.crt \
-CAkey internal-intermediateCA.key \
-CAcreateserial -out ${DOMAIN}.crt \
-days 365 -sha256
cat ${DOMAIN}.crt internal-intermediateCA.crt > ${DOMAIN}-fullchain.crt
EOF
chmod +x issue-cert.sh
./issue-cert.sh internal.example.com시나리오 3: 인증서 갱신
# 기존 개인키는 유지하고 인증서만 갱신
openssl req -new -key server.key -out server-renewal.csr
# CA에서 서명 (동일한 과정)
openssl x509 -req -in server-renewal.csr \
-CA intermediateCA.crt -CAkey intermediateCA.key \
-CAcreateserial -out server-new.crt \
-days 365 -sha256
# 무중단 배포: 새 인증서 적용 후 Nginx reload
sudo cp server-new.crt /etc/nginx/certs/server.crt
sudo nginx -s reload보안 권장사항
개인키 관리
# 개인키 권한 설정 (소유자만 읽기)
chmod 400 server.key
chown root:root server.key
# 비밀번호 보호된 개인키 사용
openssl genrsa -aes256 -out server.key 2048
# 비밀번호 제거 (자동화 필요 시)
openssl rsa -in server.key -out server-nopass.key강력한 알고리즘 사용
# SHA-256 이상 사용 (SHA-1은 deprecated)
-sha256 또는 -sha384
# RSA는 최소 2048-bit (권장 4096-bit for CA)
-newkey rsa:2048
# ECDSA는 P-256 이상
-newkey ec:<(openssl ecparam -name prime256v1)인증서 유효 기간
- Root CA: 10-20년
- Intermediate CA: 5-10년
- End-entity: 1년 (Let's Encrypt는 90일)
- 짧을수록 안전 (손상 시 피해 최소화)트러블슈팅
“unable to get local issuer certificate”
# 원인: Root CA를 찾을 수 없음
# 해결: -CAfile로 Root CA 지정
openssl verify -CAfile rootCA.crt server.crt“certificate has expired”
# 확인
openssl x509 -in server.crt -noout -dates
# 해결: 인증서 갱신“certificate signature failure”
# 원인: 서명이 유효하지 않음 (위조 또는 체인 손상)
# 확인
openssl verify -CAfile rootCA.crt -untrusted intermediateCA.crt server.crt“Hostname mismatch”
# 원인: 인증서 CN/SAN에 호스트명이 없음
# 확인
openssl x509 -in server.crt -text | grep -A 1 "Subject Alternative Name"
# 해결: SAN에 도메인 추가하여 재발급Private key와 Certificate 불일치
# MD5 해시 비교
openssl rsa -noout -modulus -in server.key | openssl md5
openssl x509 -noout -modulus -in server.crt | openssl md5
# 다르면 → 쌍이 아님, 다시 생성 필요유용한 One-liner
# 인증서 만료일까지 남은 일수
echo | openssl s_client -connect example.com:443 2>/dev/null | \
openssl x509 -noout -enddate
# 여러 서버 인증서 만료일 확인
for host in example.com google.com github.com; do
echo -n "$host: "
echo | openssl s_client -connect $host:443 2>/dev/null | \
openssl x509 -noout -dates | grep notAfter
done
# PEM 인증서를 한 줄로 (JWT 등에서 사용)
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' server.crt
# 인증서 체인에서 각 인증서 Subject 출력
openssl crl2pkcs7 -nocrl -certfile fullchain.crt | \
openssl pkcs7 -print_certs -noout정리
개인키 생성
# RSA
openssl genrsa -out server.key 2048
# ECDSA
openssl ecparam -genkey -name prime256v1 -out server.key인증서 발급 흐름
1. Private Key 생성
2. CSR 생성 (개인키로)
3. CA에 CSR 제출
4. CA가 서명하여 Certificate 발급
5. Certificate + Intermediate CA → Chain 파일 구성검증
# 형식 확인
openssl x509 -in server.crt -text -noout
# 체인 검증
openssl verify -CAfile rootCA.crt -untrusted intermediateCA.crt server.crt
# 서버 연결 테스트
openssl s_client -connect example.com:443Self-Signed vs CA-Signed
| 구분 | Self-Signed | CA-Signed |
|---|---|---|
| 용도 | 개발/테스트 | 프로덕션 |
| 신뢰 체인 | 없음 | Root CA까지 |
| 브라우저 경고 | 있음 | 없음 (신뢰된 CA) |
| 비용 | 무료 | 유료 (또는 Let’s Encrypt) |
다음 단계
- Let’s Encrypt: 무료 자동 갱신 인증서 (Certbot 사용)
- ACME 프로토콜: 인증서 자동화 표준
- HSM (Hardware Security Module): CA 개인키 안전 보관
- Certificate Transparency: 인증서 발급 투명성 로그