자바스크립트를 활성화 해주세요

p059 Openssl 로 인증서 가지고 놀기

 ·  ☕ 6 min read

Self-Signed Certificate

openssl에서 인증서를 만드는 기본적인 방법은

  • key -> csr -> crt 로 만드는 것이 일반적입니다.
  • openssl 명령으로는 openssl genrsa -> openssl req -> openssl x509 입니다.
  • crt의 포맷은 바이너리 crt, 읽을 수 있는 pem, 또다른 포맷인 der 3가지가 있습니다.

기본적인 방법으로 만들어 보면 다음과 같습니다.

openssl genrsa -out private.key 2048
openssl req -new -key private.key -out server.csr
openssl x509 -req -in server.csr -signkey private.key -out server.pem -outform PEM

단축버전으로는 다음과 같음 명령어가 있습니다.

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365

csr 정보

csr안에는 key정보 이외에 Subject라는 정보가 함께 들어갑니다.
csr 만들 때, 직접 입력하려면 다음과 같이 -subj 옵션을 함께 사용합니다.

openssl req -new -key private.key -out server.csr -config openssl.cnf -subj "/C=US/ST=California/L=San Francisco/O=My Company, Inc./CN=*.*.tkim.info/"

Subject 항목에는 다음과 같은 내용이 들어갑니다.

String  X.500 AttributeType
------------------------------
CN      commonName
L       localityName
ST      stateOrProvinceName
O       organizationName
OU      organizationalUnitName
C       countryName
STREET  streetAddress
DC      domainComponent
UID     userid

내용은 RFC2253 LDAP Distinguished Names 에 정의되어 있습니다. 정의에 따라서 복수개의 Common Name을 설정할 수 있습니다.
복수개를 사용하는 경우는 the Subject Alternative Name (SAN) Certificate 라고 부릅니다.

Subject의 예는 다음과 같습니다.

  • “CN=Patti Fuller,OU=UserAccounts,DC=corp,DC=contoso,DC=com”
  • “CN=Patti Fuller”
  • E=patti.fuller@contoso.com,CN=Patti Fuller”

what is subject in certificate?

The subject field identifies the entity associated with the public key stored in the subject public key field. The subject name MAY be carried in the subject field and/or the subjectAltName extension.

Subject (Distinguished Name)
“CN=yourname” or “O=yourorganization”.”
“CN=Mark Sutton, OU=Developers, O=Mycompany C=UK”
“CN=Patti Fuller,OU=UserAccounts,DC=corp,DC=contoso,DC=com”
“UID=12345,N=Kurt Zeilenga,OU=Engineering,L=Redwood Shores”

openssl help

openssl genrsa

비밀키(private key)를 생성하는 방법

openssl genrsa -out private.key 2048

실행결과는 다음과 같습니다.

root on goorm in ~
❯ openssl genrsa -out private.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
....................................................................+++++
................+++++
e is 65537 (0x010001)

확인

file private.key
root on goorm in ~
❯ file private.key
private.key: PEM RSA private key

다른 사람은 접근하지 못하게 권한 변경

chmod 400 private.key

비밀키(private key)로 공개키(public key)만들기

openssl rsa -in private.key -pubout -out public.key
root on goorm in ~
❯ openssl rsa -in private.key -pubout -out public.key
writing RSA key

비밀키를 암호를 넣어서 생성하는 방법

openssl genrsa -out private.key -aes256 2048
root on goorm in ~
❯ openssl genrsa -out private.key -aes256 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
........................+++++
.........................+++++
e is 65537 (0x010001)
Enter pass phrase for private.key:
Verifying - Enter pass phrase for private.key:

openssl s_client

TLS 서버에 접속해 보기

openssl s_client -connect gmo.jp:443
❯ openssl s_client -connect gmo.jp:443
CONNECTED(00000005)
depth=2 OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
verify return:1
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Extended Validation CA - SHA256 - G3
verify return:1
depth=0 businessCategory = Private Organization, serialNumber = 0110-01-029526, jurisdictionC = JP, C = JP, ST = Tokyo, L = Shibuya-ku, street = "26-1,Sakuragaoka-cho", OU = Business Division, O = "GMO internet,Inc.", CN = www.gmo.jp
verify return:1
---
Certificate chain
 0 s:businessCategory = Private Organization, serialNumber = 0110-01-029526, jurisdictionC = JP, C = JP, ST = Tokyo, L = Shibuya-ku, street = "26-1,Sakuragaoka-cho", OU = Business Division, O = "GMO internet,Inc.", CN = www.gmo.jp
   i:C = BE, O = GlobalSign nv-sa, CN = GlobalSign Extended Validation CA - SHA256 - G3
 1 s:C = BE, O = GlobalSign nv-sa, CN = GlobalSign Extended Validation CA - SHA256 - G3
   i:OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign

실행결과만 간략하게 보기

openssl s_client -connect gmo.jp:443 -brief
❯ openssl s_client -connect gmo.jp:443 -brief
CONNECTION ESTABLISHED
Protocol version: TLSv1.2
Ciphersuite: ECDHE-RSA-AES256-GCM-SHA384
Peer certificate: businessCategory = Private Organization, serialNumber = 0110-01-029526, jurisdictionC = JP, C = JP, ST = Tokyo, L =
Shibuya-ku, street = "26-1,Sakuragaoka-cho", OU = Business Division, O = "GMO internet,Inc.", CN = www.gmo.jp
Hash used: SHA256
Signature type: RSA
Verification: OK
Supported Elliptic Curve Point Formats: uncompressed
Server Temp Key: ECDH, P-256, 256 bits

tls 버전을 지정해서 접속해보기

openssl s_client -connect gmo.jp:443 -tls1 -brief  # NG
openssl s_client -connect gmo.jp:443 -tls1_1 -brief  # NG
openssl s_client -connect gmo.jp:443 -tls1_2 -brief  # OK
openssl s_client -connect www.example.com:443 -tls1 -brief  # OK
openssl s_client -connect www.example.com:443 -tls1_1 -brief  # OK

인증서 체인을 보기
root CA 에 도달하기까지의 인증서 체인을 살펴봅니다.

openssl s_client -connect www.example.com:443 -showcerts < /dev/null
root on goorm in ~ took 5s
❯ openssl s_client -connect www.example.com:443 -quiet < /dev/null
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 SHA2 Secure Server CA
verify return:1
depth=0 C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, OU = Technology, CN = www.example.org
verify return:1

인증서 체인을 간략하게 봅니다.
openssl s_client -connect www.example.com:443 -quiet

TLS 메시지를 표시합니다.
TLS 서버와 클라이언트가 주고 받는 메시지를 표시합니다.
openssl s_client -connect www.example.com:443 -msg
openssl s_client -connect www.example.com:443 -msg |grep -e “»>” -e “«<”

root on goorm in ~ took 1m
❯ openssl s_client -connect www.example.com:443 -msg |grep -e ">>>" -e "<<<"
>>> ??? [length 0005]
>>> TLS 1.3, Handshake [length 0138], ClientHello
<<< ??? [length 0005]
<<< TLS 1.3, Handshake [length 0058], ServerHello
>>> ??? [length 0005]
>>> TLS 1.3, ChangeCipherSpec [length 0001]
>>> ??? [length 0005]
>>> TLS 1.3, Handshake [length 0159], ClientHello
<<< ??? [length 0005]
<<< ??? [length 0005]
<<< TLS 1.3, Handshake [length 009b], ServerHello
<<< ??? [length 0005]
<<< TLS 1.3 [length 0001]
<<< TLS 1.3, Handshake [length 0006], EncryptedExtensions
<<< ??? [length 0005]
<<< TLS 1.3 [length 0001]
<<< TLS 1.3, Handshake [length 0fa6], Certificate
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 SHA2 Secure Server CA
verify return:1
depth=0 C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, OU = Technology, CN = www.example.org
verify return:1
<<< ??? [length 0005]
<<< TLS 1.3 [length 0001]
<<< TLS 1.3, Handshake [length 0108], CertificateVerify
<<< ??? [length 0005]
<<< TLS 1.3 [length 0001]
<<< TLS 1.3, Handshake [length 0034], Finished
>>> ??? [length 0005]
>>> TLS 1.3 [length 0001]
>>> TLS 1.3, Handshake [length 0034], Finished
<<< ??? [length 0005]
<<< TLS 1.3 [length 0001]
<<< TLS 1.3, Handshake [length 00d9], NewSessionTicket
<<< ??? [length 0005]
<<< TLS 1.3 [length 0001]
<<< TLS 1.3, Handshake [length 00d9], NewSessionTicket
<<< ??? [length 0005]
<<< TLS 1.3 [length 0001]
<<< TLS 1.3, Alert [length 0002], warning close_notify
>>> ??? [length 0005]
>>> TLS 1.3 [length 0001]
>>> TLS 1.3, Alert [length 0002], warning close_notify

root on goorm in ~ took 1m1s
❯

증명서를 파일로 저장하기

openssl s_client -connect www.example.com:443 -showcerts < /dev/null 2>/dev/null| sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'> example.pem

openssl s_server 커맨드

사전작업(비밀키, 서버인증서 작성)

필요한 사전작업은 다음의 파일들을 준비하는 것입니다. 다시 한 번, 절차는 key -> csr -> crt 순입니다.

  • private.key:비밀키
  • server.csr:인증서요구 Distinguished Name 정보가 들어 있습니다.
  • server.crt:인증서

(1) private.key

openssl genrsa -out private.key 2048

(2) server.csr

# openssl req -new -key private.key -out server.csr
# openssl req -new -key private.key -out server.csr -subj "/C=JP/ST=Tokyo/L=Setagaya/O=My Company, Inc./CN=*.*.tkim.info/"
openssl req -new -key private.key -out server.csr -subj "/CN=KUBERNETES-CA/DC=TKIM/DC=INFO"

만일 Can’t load /home/ubuntu/.rnd into RNG 의 에러가 난다면, /etc/ssl/openssl.cnf 의 이하의 라인을 삭제하면 됩니다. openssl의 버전에 따라 발생하는 경우가 있습니다.

vim /etc/ssl/openssl.cnf
# RANDFILE              = $ENV::HOME/.rnd

(3) server.crt

openssl x509 -req -in server.csr -signkey private.key -out server.crt

윈도우즈에서는 openssl의 상태에 따라서 정상작동하지 않을 수도 있습니다.

root on goorm in ~
❯ ls server.crt private.key
private.key  server.crt

TSL 서버를 기동합니다.

root on goorm in ~
❯ openssl s_server -cert server.crt -key private.key
Using default temp DH parameters
ACCEPT

다른 터미널에서 작동하는 지 확인해 봅니다.

root on goorm in ~
❯ lsof -i:4433 -P
COMMAND PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
openssl 786 root    5u  IPv6 6158796      0t0  TCP *:4433 (LISTEN)

클라이언트로 접속해봅니다. 커맨드는 openssl s_client -connect localhost:4433

root on goorm in ~
❯ openssl s_client -connect localhost:4433
CONNECTED(00000005)
depth=0 CN = KUBERNETES-CA, DC = TKIM, DC = INFO
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = KUBERNETES-CA, DC = TKIM, DC = INFO
verify return:1
---
Certificate chain
 0 s:CN = KUBERNETES-CA, DC = TKIM, DC = INFO
   i:CN = KUBERNETES-CA, DC = TKIM, DC = INFO
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDDzCCAfcCFFhHUO7ZrAswd5RPHPxL1liMjxBZMA0GCSqGSIb3DQEBCwUAMEQx
FjAUBgNVBAMMDUtVQkVSTkVURVMtQ0ExFDASBgoJkiaJk/IsZAEZFgRUS0lNMRQw
EgYKCZImiZPyLGQBGRYESU5GTzAeFw0yMDA5MjcwNTQyMzhaFw0yMDEwMjcwNTQy
MzhaMEQxFjAUBgNVBAMMDUtVQkVSTkVURVMtQ0ExFDASBgoJkiaJk/IsZAEZFgRU
S0lNMRQwEgYKCZImiZPyLGQBGRYESU5GTzCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBALK7+tV5yqfOHhjIzF1x2MX9P4vKFF+rFUiT3AfB+1Erztn4u9sH

잘 접속이 됩니다. 기본포트가 4433 이므로 443 포트로 TSL서버를 실행해 봅니다.

openssl s_server -accept 443 -cert server.crt -key private.key

www 서버로 작동시켜 봅니다.

echo "hello world" > index.html
firewall-cmd --add-port=443/tcp
openssl s_server -accept 443 -cert server.crt -key private.key -WWW

서버에 접속해 봅니다.

curl -k https://localhost:443/index.html

서버 인증서의 정보를 보기

먼저 인증서를 PEM으로 저장합니다.

openssl s_client -connect gmo.jp:443 -showcerts < /dev/null 2>/dev/null| sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'> gmo.pem

저장한 pem 정보를 텍스트로 출력합니다.

openssl x509 -in gmo.pem -noout -text

결과는 다음과 같습니다.

root on goorm in ~
❯ openssl x509 -in gmo.pem -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            28🆎b8:56:bc:8a:ca:dc:4a:72:52:c6
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = BE, O = GlobalSign nv-sa, CN = GlobalSign Extended Validation CA - SHA256 - G3
        Validity
            Not Before: Nov 11 06:41:07 2019 GMT
            Not After : Feb  3 07:21:02 2021 GMT
        Subject: businessCategory = Private Organization, serialNumber = 0110-01-029526, jurisdictionC = JP, C = JP, ST = Tokyo, L = Shibuya-ku, street = "26-1,Sakuragaoka-cho", OU = Business Division, O = "GMO internet,Inc.", CN = www.gmo.jp
        Subject Public Key Info:
...

openssl x509

PEM에서 Subject만 확인하기

openssl x509 -in example.pem -noout -subject

PEM에서 시리얼번호만 확인하기

openssl x509 -in example.pem -noout -serial

PEM에서 중간 CA만만 확인하기
openssl x509 -in example.pem -noout -issuer

인증서에서 공개키를 추출하기
openssl x509 -in example.pem -noout -pubkey

PEM형식 인증서 만들기 (-outform PEM)

보통은 key -> csr -> crt 인데, key -> csr -> pem 을 합니다.

openssl genrsa -out private.key 2048
openssl req -new -key private.key -out server.csr
openssl x509 -req -in server.csr -signkey private.key -out server.pem -outform PEM
ls server.pem
file server.pem
openssl x509 -req -in server.csr -signkey private.key -out server.crt

마찬가지로 DER형식으로도 만들 수 있습니다.(-outform DER)

openssl x509 -req -in server.csr -signkey private.key -out server.der -outform DER
ls server.der
file server.der

DER형식에서 PEM형식으로도 만들 수 있습니다.(-inform,-outform)

ls server.der
openssl x509 -in server.der -inform DER -out server.pem -outform PEM
ls server.pem

openssl ciphers

사용할 수 있는 암호스위트를 확인합니다.
openssl ciphers -v

root on goorm in ~
❯ openssl ciphers -v
TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH       Au=RSA  Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=CHACHA20/POLY1305(256) Mac=AEAD
...

openssl dgst

다이제스트를 계산합니다.

fallocate -l 1K 1K.txt
ls -l 1K.txt
openssl dgst -sha512 1K.txt
openssl dgst -rsa1 1K.txt

root on goorm in ~
❯ fallocate -l 1K 1K.txt

root on goorm in ~
❯ ls -l 1K.txt
-rw-rw-r-- 1 root root 1024 Sep 27 09:40 1K.txt

root on goorm in ~
❯ openssl dgst -sha512 1K.txt
SHA512(1K.txt)= 8efb4f73c5655351c444eb109230c556d39e2c7624e9c11abc9e3fb4b9b9254218cc5085b454a9698d085cfa92198491f07a723be4574adc70617b73eb0b6461

echo “1234567890” > test.txt
openssl genrsa -out priv.key 2048
openssl rsa -in priv.key -pubout -out pub.key
openssl rsautl -encrypt -pubin -inkey pub.key -in test.txt -out test.txt.enc
rm test.txt
openssl rsautl -decrypt -inkey priv.key -in test.txt.enc -out test.txt
cat test.txt

openssl s_server 커맨드와 openssl s_client 커맨드는 연결이 된 후에는 입력을 기다리는데, 이 상태에서 문자열을 전달하는 것이 가능합니다.

key, csr, crt가 일치하는 지 확인하는 방법

openssl rsa -noout -modulus -in private.key |openssl md5
openssl req -noout -modulus -in server.csr |openssl md5
openssl x509 -noout -modulus -in server.crt |openssl md5

root on goorm in ~
❯ openssl rsa -noout -modulus -in private.key |openssl md5
(stdin)= c462527182d8d396e31365d36bc54aa8

root on goorm in ~
❯ openssl req -noout -modulus -in server.csr |openssl md5
(stdin)= c462527182d8d396e31365d36bc54aa8

root on goorm in ~
❯ openssl x509 -noout -modulus -in  server.crt |openssl md5
(stdin)= c462527182d8d396e31365d36bc54aa8

openssl req -new
-key “$PRIVATE_KEY”
-sha256
-config “$OPTIONS_FILE”
-subj “/C=US/ST=California/L=San Francisco/O=My Company, Inc./CN=*.*.$DOMAIN/”
-out “$CSR_FILENAME”

openssl.cnf path

openssl.cnf는 어디에 있지?
/usr/lib/ssl/openssl.cnf

❯ find / -name openssl.cnf
/usr/lib/ssl/openssl.cnf
/etc/ssl/openssl.cnf

root on goorm in ~
❯ openssl version -d
OPENSSLDIR: "/usr/lib/ssl"

아 이렇게 보는거야?

/usr/bin/openssl version -a | grep OPENSSLDIR

윈도우즈라면
C:/Strawberry/c/ssl/openssl.cnf
C:\Strawberry\c\bin\openssl.EXE

C:\temp>openssl version -a
OpenSSL 1.1.1d  10 Sep 2019
built on: Fri Feb  7 14:26:55 2020 UTC
platform: mingw64
options:  bn(64,64) rc4(16x,int) des(long) idea(int) blowfish(ptr)
compiler: gcc -m64 -Wall -O3 -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DVPAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPOLY1305_ASM -DUNICODE -D_UNICODE -DWIN32_LEAN_AND_MEAN -D_MT -DZLIB -DNDEBUG -I/z/extlib/_2020Q1-small__/include -D__MINGW_USE_VC2005_COMPAT -DOPENSSLBIN="\"/z/extlib/_2020Q1-small__/bin\""
OPENSSLDIR: "Z:/extlib/_2020Q1-small__/ssl"
ENGINESDIR: "Z:/extlib/_2020Q1-small__/lib/engines-1_1"
Seeding source: os-specific

마치며

이상으로 openssl로도 인증서를 이리저리 다루어 보았습니다. 감사합니다.

공유하기

tkim
글쓴이
tkim
Software Engineer