Journey to Security/리눅스

OpenSSL로 Apache HTTPS 인증서 생성하기 (1) SAN, 와일드카드 인증서

Cordilog 2026. 4. 30. 23:55

웹 서비스를 운영할 때 HTTPS(HyperText Transfer Protocol Secure)는 필수다.

HTTP는 데이터를 평문으로 전송하기 때문에 스니핑(Sniffing) 위협에 노출되어 있지만, HTTPS는 SSL/TLS 프로토콜을 통해 데이터를 암호화하여 보안성을 확보한다.

 

Rocky Linux 9 환경에서 Apache 웹 서버를 이용해 단일 도메인, 멀티 도메인(SAN), 그리고 와일드카드(*) 인증서를 생성하고 적용하는 과정을 실습하고, 실무에서 인증서가 어떤 과정을 거쳐 발급되는지, 그리고 그 과정에서 핵심 역할을 하는 CSR(Certificate Signing Request)이 무엇인지도 살펴보자.

 

1. SSL/TLS 인증서의 기본 개념 및 체계

인증서는 클라이언트(브라우저)와 서버 간의 신뢰를 구축하는 디지털 문서다.

  • 개인키(Private Key): 서버만 보유하는 비밀 키로, 데이터를 복호화하거나 서명할 때 사용한다. 유출 시 보안 체계가 무너지므로 엄격한 권한 관리가 필요하다.
  • 공개키 및 인증서(Public Key & Certificate): 누구나 볼 수 있는 파일로, 서버의 신원을 증명하고 데이터를 암호화하여 서버로 보낼 때 사용한다.
  • 자가 서명 인증서(Self-Signed Certificate): 공인된 인증기관(CA)이 아닌, 서버 관리자가 직접 서명한 인증서다. 암호화 통신은 가능하지만 브라우저가 신뢰하지 않아 경고 메시지가 발생한다.

 

🟢 PKI(Public Key Infrastructure, 공개키 기반 구조)

/etc/pki 디렉토리는 리눅스 시스템에서 보안 심장부와 같은 곳이다.

 

  • /etc/pki/tls: 가장 핵심적인 폴더다. Apache(httpd)나 Nginx 같은 웹 서버, 메일 서버 등이 SSL/TLS 암호화 통신을 할 때 필요한 파일들이 모여 있다.
    • certs/: 공개되어도 상관없는 인증서(.crt) 파일들이 저장된다. localhost.crt 같은 기본 인증서나 우리가 실습에서 만든 인증서들이 여기 위치한다.
    • private/: 절대로 유출되면 안 되는 개인키(.key) 파일들이 저장된다. 보안을 위해 보통 root 사용자만 접근 가능하도록 설정되어 있다.
  • /etc/pki/ca-trust: 시스템 전체가 "이 인증기관은 믿을 수 있다"라고 판단하는 근거가 되는 곳이다. 사내 사설 인증서나 자가 서명 인증서를 시스템 전체에 신뢰시키고 싶을 때 이 폴더의 설정을 변경한다.
  • /etc/pki/rpm-gpg: 리눅스 패키지(RPM)를 설치할 때, 이 패키지가 변조되지 않았는지 확인하기 위한 GPG 공개키가 들어 있다. dnf나 yum으로 소프트웨어를 설치할 때 "Key를 가져오시겠습니까?"라고 묻는다면 보통 이 폴더와 관련이 있다.

🟢 인증서 등록 시 주의사항

 

  • 개인키(.key): 반드시 root 소유여야 하며, 권한은 600(-rw-------)이어야 한다.
  • 인증서(.crt): 공개용이므로 보통 644(-rw-r--r--) 권한을 가진다.

 

 

2. HTTPS 단일 도메인 인증서 생성하기

가장 기본적인 형태의 인증서로, 하나의 도메인에 대해서만 유효성을 갖는다.

1️⃣ 인증서 및 개인키 생성

openssl 도구를 사용하여 RSA 2048비트 개인키와 1년 유효기간의 자가 서명 인증서를 동시에 생성한다.

# openssl req 명령을 통한 키와 인증서 생성
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/pki/tls/private/key-practice-123.com.key \
-out /etc/pki/tls/certs/key-practice-123.com.crt

[명령어 옵션]

  • req: CSR(인증서 서명 요청) 관련 작업을 수행한다.
  • -x509: 이 옵션이 있으면 CSR을 만들지 않고 직접 '자가 서명 인증서'를 생성한다. (X.509 표준 형식에 맞는 완성된 인증서를 바로 만듬)
  • -nodes: 'no DES'의 약자로, 개인키를 암호화하지 않는다. 서버 재시작 시 비밀번호 입력을 생략하기 위함이다.
  • -newkey rsa:2048: 2048비트 RSA 알고리즘(비대칭 암호화) 기반의 새로운 공개키-개인키 쌍을 생성한다.
  • -keyout / -out: 각각 개인키와 인증서가 저장될 경로를 지정한다.

 

🟢 권한 설정

개인키는 소유자(root)만, 인증서는 모두 읽기 가능하도록 권한을 설정한다.

chmod 600 /etc/pki/tls/private/key-practice-123.com.key
chmod 644 /etc/pki/tls/certs/key-practice-123.com.crt

 

 2️⃣ 인증서 정보 입력

명령어 실행 시 나타나는 질문에 적절하게 입력한다. (모두 엔터 쳐서 넘어가도 무방하다.)

  • Country Name: KR
  • State or Province Name: Seoul
  • Locality Name: Seoul
  • Organization Name:
  • Organizational Unit Name: 
  • Common Name: 예) key-practice-123.com (중요: 접속 도메인과 일치해야 함)
  • E-mail Address:

 

🟢 Common Name 

최신 브라우저 (Chrome, Firefox 등)에서는 인증서에 SAN 정보가 있다면 CN 정보는 완전히 무시한다.

그럼에도 불구하고 다음과 같은 이유로 CN 정보는 생략하지 않고 적어주는 것이 바람직하다. 

 

① 인증서의 대표 이름 (Display Name)

인증서를 생성하고 나면 서버나 클라이언트 시스템(Windows 인증서 관리자 등)에서 인증서 목록을 보게 된다.

이때 CN이 비어 있으면 인증서 이름이 공란으로 나오거나 파일명으로만 표시되어, 어떤 도메인용 인증서인지 한눈에 파악하기 힘들어진다.

 

② 호환성 문제 (Legacy Support)

웹 브라우저가 아닌 특정 애플리케이션, 구형 자바(Java) 버전, 혹은 오래된 네트워크 장비들은 SAN 필드를 읽지 못하는 경우가 있다.

이런 장비들은 CN이 비어 있으면 인증서가 유효하지 않다고 판단하여 통신을 차단한다.

 

③ CA(인증기관)의 정책

공인 인증기관에 CSR을 제출할 때 CN을 비워두면, 대다수의 CA는 발급 거절을 하거나 오류를 발생시킨다. 신청서에 신청자 이름이 없는 것과 마찬가지이기 때문이다.

 

3️⃣ 암호화 전송 확인용 index 파일 준비

🟢 APM 패키지 설치 

간단한 확인용 페이지를 만들기 위해 APM 패키지를 설치한다. 

이미 설치가 되어 있다면 생략 가능하다. 

sudo dnf -y install httpd mod_ssl php php-mysqlnd mariadb mariadb-server

🟢 DB용 인코딩 설정 

인증서 생성을 연습하는 데는 굳이 필요 없는 설정이지만 한글 깨짐 방지와 이모지 인식을 위해서 apm 패키지를 묶어서 설치할 때 같이 설정해두는 것이 좋다.

 

◼ mariadb utf8 설정

vi /etc/my.cnf.d/mariadb-server.cnf

16 [mysqld]
17 datadir=/var/lib/mysql
18 socket=/var/lib/mysql/mysql.sock
19 log-error=/var/log/mariadb/mariadb.log
20 pid-file=/run/mariadb/mariadb.pid
21 character-set-server=utf8mb4
22 collation-server=utf8mb4_general_ci
vi /etc/my.cnf.d/client.cnf
7 [client]
8 default-character-set=utf8mb4

 

◼ 서비스를 영구적으로 시작한다.

systemctl enable --now httpd.service  php-fpm.service  mariadb.service

◼ 방화벽에서 http, https 서비스를 추가한다.

firewall-cmd --permanent --add-service={http,https}
firewall-cmd --reload
firewall-cmd --list-services
cockpit dhcpv6-client http https ssh

🟢 웹페이지 index 파일 생성


https 암호화 전송 확인용 페이지에 넣을 간단한 문구를 생성한다. 

echo '<?php echo "https 암호화 전송"?> ' > /var/www/html/index.php

4️⃣ Apache SSL 설정 및 적용

생성된 파일을 Apache(httpd) 웹 서버에서 HTTPS(SSL/TLS) 암호화 통신을 설정하는 환경 설정 파일(ssl.conf) 에 등록한다.

vi /etc/httpd/conf.d/ssl.conf

기존 설정을 주석 처리하고 신규 경로를 지정한다.


SSLCertificateFile /etc/pki/tls/certs/key-practice-123.com.crt
SSLCertificateKeyFile /etc/pki/tls/private/key-practice-123.com.key

 

ssl.conf 수정 후 문법 오류를 검사하고 서비스를 재시작한다.

apachectl configtest
systemctl restart httpd.service

 

5️⃣ https 접속 확인

간단하게 로컬 서버에서 체크한다.

# curl -k https://localhost

 

윈도우 브라우저에서 웹 서버에 접속해서 https가 적용되었음을 확인한다. 

https://192.168.100.139 

 

서버가 정상적으로 인증서를 브라우저에 던져주고 있기 때문에 위와 같은 화면이 뜨게 된다.

만약 설정이 안 됐다면 "연결할 수 없음" 같은 메시지가 뜨게 된다.

 

192.168.100.139(안전하지 않음)으로 이동을 클릭해 위에서 생성한 페이지가 뜨는 것을 확인한다. 

[주의 요함] 클릭 > [인증서 세부정보]를 클릭하면 인증서 뷰어가 열린다. 

 

 

위에서 생성한 대로 인증서 정보가 나오는 것을 확인할 수 있다.

📌 브라우저에서 경고가 뜨는 이유

정상적인 HTTPS 인증서로 인식되려면 브라우저가 체크하는 3가지 조건을 모두 통과해야 한다. 

  • 인증기관 신뢰성 (❌): 위의 경우 openssl로 직접 사설 인증서를 만들었으므로 브라우저가 아는 '신뢰된 루트 인증기관' 목록에는 인증서가 존재하지 않는다. 그래서 화면에 NET::ERR_CERT_AUTHORITY_INVALID라는 에러 코드가 찍힌 것이다.
  • 도메인 일치 (❌): 인증서는 이나 key-practice-123.com으로 발급되었지만 주소창에는 IP(192.168.100.139)를 쳤기 때문에 정보가 서로 달라서 경고가 뜰 수 있다.
  • 인증서 유효 기간 (✅): 실습에서 -days 365로 설정했기 때문에 이 조건은 만족되어 있는 상태다.

 

3. HTTPS 멀티 도메인(SAN) 인증서 사용하기

과거에는 Common Name(CN)에 적힌 도메인 하나만 확인했지만, 요즘 브라우저는 Subject Alternative Name(SAN) 필드에 나열된 모든 도메인을 확인한다.

멀티 도메인 인증서는 하나의 인증서 파일 안에 여러 개의 서로 다른 도메인을 포함할 수 있다.

1️⃣ SAN 설정 파일 작성

여러 도메인을 등록하기 위해 별도의 설정 파일(.cnf)이 필요하다.

다음과 같이 설정 파일을 작성한다.

vi key-practice-123_san.cnf

[req]
default_bits       = 2048
prompt              = no
default_md          = sha256
x509_extensions    = v3_req
distinguished_name = dn

[dn]
C  = KR
ST = Seoul
L  = Seoul
O  = Test Company
OU = Security Team
CN = security.key-practice-123.com

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = security.key-practice-123.com
DNS.2 = www.key-practice-123.com
DNS.3 = test.key-practice-123.com
DNS.4 = blog.key-practice-123.com

2️⃣ 인증서 생성 및 권한 설정

🟢 인증서 생성

설정 파일을 참조하여 인증서를 생성하고 보안을 위해 파일 권한을 조정한다.

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/pki/tls/private/key-practice-123-san.key \
-out /etc/pki/tls/certs/key-practice-123-san.crt \
-config /root/key-practice-123_san.cnf

🟢 권한 설정

개인키는 소유자(root)만, 인증서는 모두 읽기 가능하도록 권한을 설정한다.

chmod 600 /etc/pki/tls/private/key-practice-123-san.key
chmod 644 /etc/pki/tls/certs/key-practice-123-san.crt

 

3️⃣ 인증서 내역의 SAN 필드 확인

-text 옵션을 사용하여 SAN 필드가 정상적으로 들어갔는지 확인한다.

openssl x509 -in /etc/pki/tls/certs/key-practice-123-san.crt -text -noout

출력 내용 중 'X509v3 Subject Alternative Name' 항목을 확인한다.

 

4️⃣ Apache SSL 설정

생성된 파일을 Apache(httpd) 웹 서버에서 HTTPS(SSL/TLS) 암호화 통신을 설정하는 환경 설정 파일(ssl.conf)에 등록한다.

vi /etc/httpd/conf.d/ssl.conf

 

기존 설정을 주석 처리하고 신규 경로를 지정한다.


SSLCertificateFile /etc/pki/tls/certs/key-practice-123-san.crt
SSLCertificateKeyFile /etc/pki/tls/private/key-practice-123-san.key

 

5️⃣ ServerName 등록

ServerName은 기본적으로 '도메인 이름(또는 IP 주소)'을 의미하지만, 뒤에 포트 번호를 붙여서 어떤 포트로 들어오는 요청을 처리할지 명시하는 역할을 한다.

아파치 웹 서버는 하나의 서버(하나의 IP)에서 여러 개의 서비스를 돌릴 수 있기 때문에 ServerName에 포트를 명시해서 혼선을 막아야 한다. 

 

기본 형식: ServerName [도메인 또는 IP]:[포트번호]

 

  • 도메인/IP: 서버가 스스로를 식별할 때 사용하는 이름
  • 포트: 서버가 해당 서비스를 제공하는 통로 번호 (HTTP는 80, HTTPS는 443)
vi /etc/httpd/conf/httpd.conf

아파치 웹서버를 설치하면 기본적으로 메인 설정 파일(httpd.conf)에 ServerName이 주석으로 되어 있다.

 

위와 같이 ServerName이 설정되어 있지 않으면 아파치가 파일을 읽는 과정에서 경고를 발생시킨다.

 

따라서 서버가 자신의 도메인을 스스로 추측하지 못해서 발생하는 시스템 경고와 리다이렉션 오류를 방지하기 위해서는 메인 설정 파일을 아래처럼 수정해줘야 한다.

 

100 ServerName 127.0.0.1:80

 

6️⃣ 설정 파일 체크 및 서비스 재시작

ssl.conf 파일과 httpd.conf 파일 수정 후 문법 오류를 검사하고 서비스를 재시작한다.

apachectl configtest //또는 httpd -t
systemctl restart httpd.service

 

7️⃣ 도메인 설정 (윈도우)

우선 위에서 만든 HTTPS 인증서가 실제로 작동하는지 테스트하기 위해 가상의 환경을 구축해야 한다.

실제로 구매해서 존재하는 도메인이 아니기 때문에 로컬에서 해당 도메인이 실제 존재하는 것처럼 재현하기 위해서는 hosts 파일에 등록을 해줘야 한다. 

 

 

  • 컴퓨터의 도메인 검색 순서: 컴퓨터는 주소창에 도메인을 치면 [캐시 → hosts 파일 → DNS 서버] 순서로 IP를 찾는다.
  • hosts 파일에 IP와 도메인을 적어두면 컴퓨터는 DNS 서버에 질의하기 전에 "이 도메인은 192.168.100.139로 가라는 거구나"라고 판단하고 바로 그 IP로 접속하게 된다.

 

윈도우 cmd를 관리자 권한으로 실행해서 hosts 파일 하단에 위에 SAN으로 설정한 도메인들을 추가한다.


> notepad C:\Windows\System32\drivers\etc\hosts

192.168.100.10  security.key-practice-123.com
192.168.100.10  www.key-practice-123.com
192.168.100.10  test.key-practice-123.com
192.168.100.10  blog.key-practice-123.com

 

8️⃣ 리눅스 서버에 IP 주소 임시 등록

클라이언트 쪽 hosts 파일에 등록한 192.168.100.10 IP주소를 리눅스 서버에 임시로 등록한다.

ip a a dev ens160 192.168.100.10/24 //ip address add 192.168.100.10/24 dev ens160

 

ip -br -4 a 명령어로 IP 주소가 추가된 것을 확인한다.

9️⃣ https 접속 확인

이렇게 저장하면 클라이언트에서 위 도메인주소 4개를 입력했을 때 모두 192.168.100.10로 간다.

 

 

리눅스 서버에서도 테스트를 해본다.

curl -k https://localhost
curl -k 192.168.100.10
curl -k security.key-practice-123.com
curl -k www.key-practice-123.com
curl -k test.key-practice-123.com
curl -k blog.key-practice-123.com

 

4. HTTPS 와일드카드(*) 인증서 사용하기

 

와일드카드 인증서는 특정 도메인의 모든 하위 도메인(예: *.naver.com)을 하나의 인증서로 보호할 때 사용한다.

 

와일드카드(*)는 한 단계의 서브도메인만 커버한다.

즉, *.key-practice-123.com a.b. key-practice-123.com과 같은 2단계 서브도메인은 보호하지 못한다.

또한, 루트 도메인(key-practice-123.com) 역시 보호 대상에서 제외되는 경우가 많으므로 SAN 설정 시 루트 도메인을 따로 명시해주는 것이 관례다.

1️⃣ 와일드카드 인증서 설정파일 작성

위에서 만들어 둔 SAN 인증서 파일을 복사해서 와일드카드 인증서로 수정하는 것으로 한다.

cp key-practice-123_san.cnf key-practice-123_wc.cnf 
vi key-practice-123_wc.cnf

 

CN 부분과 [alt_names] 부분만 다음과 같이 수정한다.

 

[dn]
C  = KR
ST = Seoul
L  = Seoul
O  = Test Company
OU = Security Team
CN = *.key-practice-123.com

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = key-practice-123.com
DNS.2 = *.key-practice-123.com

 

2️⃣ 인증서 생성 및 권한 설정

이후 생성 및 적용 과정은 SAN 방식과 동일하므로 자세한 설명은 생략한다.

 

인증서 및 키 생성

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/pki/tls/private/key-practice-123-wc.key \
-out /etc/pki/tls/certs/key-practice-123-wc.crt \
-config /root/key-practice-123_wc.cnf

 

권한 설정

chmod 600 /etc/pki/tls/private/key-practice-123-wc.key
chmod 644 /etc/pki/tls/certs/key-practice-123-wc.crt

 

3️⃣ 인증서 내역의 SAN 필드 확인

openssl x509 -in /etc/pki/tls/certs/key-practice-123-wc.crt -text -noout

 

4️⃣ Apache SSL 설정

vi /etc/httpd/conf.d/ssl.conf

 

SSLCertificateFile /etc/pki/tls/certs/key-practice-123-wc.crt
SSLCertificateKeyFile /etc/pki/tls/private/key-practice-123-wc.key

 

5️⃣ 설정 파일 체크 및 서비스 재시작

apachectl configtest //또는 httpd -t
systemctl restart httpd.service

 

 

6️⃣ https 접속 확인

위에서 클라이언트 쪽 hosts 파일에 등록해 둔 4개의 도메인을 테스트해본다.

security.key-practice-123.com
www.key-practice-123.com
test.key-practice-123.com
blog.key-practice-123.com

 

모두 새로 만든 와일드카드 인증서(*.key-practice-123.com)의 범위 안에 포함되므로 문제 없이 접속이 된다.

하지만 이 외에 다른 서브도메인을 테스트하려면 hosts 파일에 추가하고 해야 한다.

윈도우나 리눅스의 hosts 파일은 와일드카드(*)를 인식하지 못하기 때문에 인증서를 와일드카드로 만들었다고 해서 hosts 파일에도 192.168.100.10 *.key-practice-123.com 처럼 적으면 작동하지 않는다.

반드시 정확한 전체 이름(FQDN)을 적어줘야 한다.

 

 

5. 참고: HTTPS 인증서 정상 인식 조건

실습을 통해 확인했듯이, 브라우저가 인증서를 "안전함"으로 인식하기 위해서는 다음 조건이 충족되어야 한다.

  1. 도메인 일치: 접속한 URL 주소가 인증서의 CN 또는 SAN 리스트에 있어야 한다.
  2. 유효 기간: 현재 시간이 인증서의 Not Before와 Not After 사이에 있어야 한다.
  3. 신뢰할 수 있는 기관: 인증서 체인의 최상단에 있는 루트 인증서가 브라우저/OS의 신뢰 목록에 등록되어 있어야 한다.

자가 서명 인증서는 3번 조건을 만족하지 못하므로 수동 등록 과정이 필요하지만, 실제 운영 환경에서는 Let's Encrypt와 같은 공인 CA를 통해 3번 조건을 자동으로 만족시키는 방식을 권장한다.

 

이어서 다음 포스팅을 통해 자가 서명 인증서를 OS의 신뢰 목록에 등록하는 것을 실습해 본다.