Chistory

OpenSSL root CA 인증기관을 만들어보자. 본문

개발 & CS/Linux

OpenSSL root CA 인증기관을 만들어보자.

Chistory 2020. 1. 6. 15:01

간혹 웹서비스나 TLS 통신 등을 구축할 때, 인증서가 필요하다. 

오늘날의 인증 체계는, x509 기반의 인증서를 사용한다. 흔히 보는 ---BEGIN.....--- 이런식으로 시작하고, 암호화된 문자열이 있는 그 파일을 의미한다. 

이런 인증서들은, 일종의 체인을 이뤄서 root CA로부터 인증을 받고, 그것들에 대한 key와 인증서를 가지고 웹 서버를 구동시키는 식으로 활용된다. 신뢰할 수 있는 root CA 로부터 인증을 받은 인증서들은, 웹 브라우저에서 안전한 페이지로 표시되곤 한다. 이러한 신뢰할 수 있는 인증 기관으로부터의 인증은, 도메인 기반으로 이루어지므로 일단 개인 소유의 도메인이 필요하며, Lets Encrypt 의 3개월 단위의 무료 인증이 아닌 경우, 비싼 요금을 내고 인증서를 발급받아야 한다.

물론 단순히 패킷에 대한 암호화가 필요한 경우, root CA를 직접 구축하여, 생성하고 폐기할 수 있다. 오늘은 이러한 root CA를 만드는 작업을 공유하려 한다. 다양한 방식이 있지만, 쉘 스크립트를 이용하여 간단하게 구축을 진행할 것이다.

먼저 간단한 리눅스 환경이 필요하다. 사전 설치가 필요한 것은, openssl 패키지 정도이다. 

# CentOS
sudo yum install openssl

# Ubuntu
sudo apt-get install openssl

그리고 간단한 폴더, 파일 구조가 필요하다. 

cd
mkdir rootca
cd rootca
mkdir newcert
mkdir oldcert
mkdir private
mkdir rootca
mkdir crl
touch index.txt
touch serial
touch crlnumber

인증서 생성에 관련된 주요 파일들을 보면, 먼저 인증 요청서가 있고, key가 있다. 여기에 이 두가지를 이용해 생성되는 인증서 파일, 그리고 Root CA에서 관리하는 인증서 폐기 목록이 있다. 인증 요청서는 보통 csr 파일이라고 부른다. 

먼저 root CA 인증서에 사용될 csr을 생성해야 한다. 다음 명령어로 key 파일을 만든다.

openssl genrsa -aes256 -out rootca/rootca.key 2048

pass phrase 를 입력하라는 프롬프트가 나오면, 원하는 비밀번호를 입력한다. 

다음으로 인증기관을 어떻게 설정할 지에 대한 설정 파일이 필요하다. 

vim rootca_openssl.conf

#아래는 파일 내용
[ ca ]
default_ca      = CA_default            # The default ca section

[ CA_default ]

dir             = .           # Where everything is kept
certs           = $dir/certs            # Where the issued certs are kept
crl_dir         = $dir/crl              # Where the issued crl are kept
database        = $dir/index.txt        # database index file.
#unique_subject = no                    # Set to 'no' to allow creation of
# several ctificates with same subject.
new_certs_dir   = $dir/newcerts         # default place for new certs.

certificate     = $dir/rootca.crt       # The CA certificate
serial          = $dir/serial           # The current serial number
crlnumber       = $dir/crlnumber        # the current crl number
# must be commented out to leave a V1 CRL
crl             = $dir/crl.pem          # The current CRL
private_key     = $dir/private/cakey.pem# The private key
RANDFILE        = $dir/private/.rand    # private random number file

x509_extensions = usr_cert              # The extentions to add to the cert

# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt        = ca_default            # Subject Name options
cert_opt        = ca_default            # Certificate field options

default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md      = sha1                  # which md to use.
preserve        = no                    # keep passed DN ordering

policy          = policy_match

[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
default_bits            = 2048
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
attributes              = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert

string_mask = default

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = KR
countryName_min                 = 2
countryName_max                 = 2

stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = YourOrganizationName

localityName                    = Locality Name (eg, city)

0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = YourOrganizationName

# we can do this but it is not needed normally
#1.organizationName             = Second Organization Name (eg, company)
#1.organizationName_default     = World Wide Web Pty Ltd

organizationalUnitName          = Organizational Unit Name (eg, section)
#organizationalUnitName_default =

commonName                      = Common Name (eg, YOUR name) #이름 등을 적는다.
commonName_max                  = 64

emailAddress                    = Email Address #메일 주소를 적는다.
emailAddress_max                = 64

[ req_attributes ]
challengePassword               = A challenge password
challengePassword_min           = 4
challengePassword_max           = 20

unstructuredName                = An optional company name

[ usr_cert ]


nsComment                       = "OpenSSL Generated Certificate"

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer

crlDistributionPoints=@cdp_section

nsCaRevocationUrl               = http://192.168.1.3/cert/carevoke
nsRevocationUrl                 = http://192.168.1.3/cert/revoke

[ v3_req ]
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always

[ crl_ext ]
authorityKeyIdentifier=keyid:always,issuer:always

[ proxy_cert_ext ]
basicConstraints=CA:FALSE
nsComment                       = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always

[crldp1_section]

[cdp_section]
URI=http://192.168.1.3/cert/crl.pem

http로 시작하는 주소는, CDP, 즉 인증서 폐기 목록 배포 포인트를 지정해주는 것이다. 웹 서버를 직접 구축하면 가능하긴 한데, 여기선 굳이 신경쓰지 않아도 된다. 만약 apache나 nginx로 웹 서버를 구성중이라면, 해당 경로를 web root로 놓으면 배포 포인트로 활용할 수 있다. 보통 웹도 쓰이지만, ldap등의 다른 체계를 사용하기도 한다.

req_distinguished_name 섹션 내부의 내용은, 필요에 따라 적절히 바꿔서 입력한다. 입력하지 않아도, 다음 단계에서 입력하라는 프롬프트가 나타나긴 한다. 

openssl req -new -key rootca/rootca.key -out rootca/rootca.csr -config rootca_openssl.conf

위 커맨드를 입력하면, root ca에 대한 CSR 파일을 생성한다. 프롬프트 안내에 따라, 인증서 생성에 필요한 요청서를 만들어준다.

아래 커맨드로 10년짜리 루트 인증서를 발급하면, 일단 인증 기관이 완성된다. 

openssl x509 -req -days 3650 -extensions v3_ca -set_serial 1 -in rootca/rootca.csr -signkey rootca/rootca.key -out rootca/rootca.crt -extfile rootca_openssl.conf

다음 커맨드로 제대로 완성이 되었나 확인해보자

openssl x509 -text -in rootca/rootca.crt

이제 이 rootca로 새로운 인증서를 찍어낼 차례다. 이 인증기관에 요청할 요청서와, private key가 필요하다. 

아래 커맨드로 새로운 csr 요청서를 생성할 수 있다. 

openssl genrsa -aes256 -out private/tmp.key 2048
openssl rsa -in private/tmp.key -out private/new.key
openssl req -new -key private/new.key -sha256 -out private/new.csr

csr을 만들었으면, 새로운 인증서에도 적용할 conf 파일을 작성한다. 

vim host.conf
#아래는 작성할 내용
[ v3_user ]
# Extensions to add to a certificate request
basicConstraints = CA:TRUE
authorityKeyIdentifier = keyid,issuer
subjectKeyIdentifier = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
## SSL 용 확장키 필드
extendedKeyUsage = serverAuth,clientAuth

[ usr_cert ]

crlDistributionPoints=@cdp_section

nsCaRevocationUrl               = http://192.168.1.3/cert/carevoke
nsRevocationUrl                 = http://192.168.1.3/cert/revoke

[cdp_section]
URI=http://192.168.1.3/cert/crl.pem

 

흔히 사용되는 옵션들이다.  마지막으로 아래 커맨드로 새로운 인증서를 생성한다. 

openssl x509 -req -days 1825 -extensions v3_user -extensions usr_cert -in private/new.csr -CA rootca/rootca.crt -CAcreateserial -CAkey rootca/rootca.key -out newcert/new.crt -extfile host.conf

이제 newcert 디렉토리에 new.crt 라는 인증서가 생성된 것을 확인할 수 있다. 

이를 간단하게 CSR로부터 인증서를 생성하고, 특정 인증서를 폐기하는 시스템을 쉘 스크립트로 작성할 수 있다. 

간단한 쉘 스크립트로, csr파일에 대한 처리를 해주는 스크립트를 만든다. 

vim cert.sh
#!/bin/bash

CUR=`pwd`
CONF="rootca_openssl.conf"
HOST_CONF="host.conf"

cd $CUR
usage(){
        echo "$0 revoke [newcert/certname]"
        echo "$0 new [csrfile] [title]"
        exit
}

if [ $# -lt 2 ]  || [ $# -gt 3 ]; then
        usage
fi



if [ "$1" = "new" ]; then
        if [ $# -ne 3 ] ; then
                usage
        fi
        openssl x509 -req -days 1825 -extensions v3_user -extensions usr_cert -in $2 -CA rootca/rootca.crt -CAcreateserial -CAkey rootca/rootca.key -out newcert/$3.crt -extfile $HOST_CONF
elif [ "$1" = "revoke" ]; then
        openssl ca -keyfile rootca/rootca.key -cert rootca/rootca.crt -revoke $2 -config $CONF
        openssl ca -keyfile rootca/rootca.key -cert rootca/rootca.crt -gencrl -out rootca/rootca.crl  -config $CONF
        mv $2 ${CUR}/oldcert

else
        usage
fi
#최초 설정 부분
#스크립트 권한 부여
chmod 755 cert.sh
echo -n "00" > crlnumber


#사용법
#인증서 생성
./cert.sh new [새로운 인증요청서] [인증서파일이름]
#인증서 폐기
./cert.sh revoke [폐기할 인증서]

인증서를 폐기할 경우, index.txt에 폐기 목록이 업데이트된다. 

인증서를 생성하면, newcert 디렉토리에 생성한 파일 이름으로 인증서가 들어간다. 

이렇게 구축한 인증서는, 다양한 서비스에서 패킷 암호화에 사용할 수 있다. 

Comments