通常我們在私人用途的服務需要憑證時可能會考慮自簽憑證(或如果服務公開在網際網路的話可以考慮 Let's Encrypt[1]),服務較多時則會考慮自簽 CA 之後再往下發。
而在一定規模的組織內部時,有可能在根 CA(Root CA)下面還有其他中介 CA(Intermediate CA),這篇就是來記錄一下要怎麼透過 OpenSSL 工具簽發中介 CA。
簽發 Root CA
首先我們在有中介 CA 之前,必須要有 Root CA,這邊會介紹怎麼簽發 Root CA:
建立 Root CA 用的 Key
建立 Key 應該是整個環節中最簡單的步驟了,利用簡單的 openssl genrsa
即可,這邊採用 AES256 加密 Key 並將長度設為 4096
(如果你不想要加上密碼,就不要下 -aes256
,但你的 Key 就少了一層保護):
$ openssl genrsa -aes256 -out ca.key 4096
Generating RSA private key, 4096 bit long modulus
# ......
e is 65537 (0x10001)
Enter pass phrase for ca.key:
Verifying - Enter pass phrase for ca.key:
$
如此一來你就成功的建立了一個給 Root CA 用的 Key,請妥善保管它(跟它的密碼),我們接下來會用這個 Key 來簽發 Root CA。
建立 Root CA
有了 Key 之後,我們就要用這把 Key 來建立 Root CA 了,這裡利用 openssl req
直接簽發 Root CA:
$ openssl req -new -x509 -key ca.key \
-days 7300 -sha256 \
-extensions v3_ca \
-out ca.pem
Enter pass phrase for ca.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:TW
State or Province Name (full name) [Some-State]:Taiwan
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Davy.TW
Organizational Unit Name (eg, section) []:Davy.TW Certificate Authority
Common Name (e.g. server FQDN or YOUR name) []:Davy.TW Root CA
Email Address []:
$
如此一來就會用剛剛的 Key 簽發一張 x509 的 Root CA 憑證,一般來說 openssl req
會需要 -config
來帶入一個設定檔,預設會使用 /etc/pki/tls/openssl.cnf
(Red Hat)/ /etc/ssl/openssl.cnf
(Ubuntu/FreeBSD),其內部會預先定義好一些設定值(例如 -extension v3_ca
的設定值)。
驗證 Root CA
簽發好 Root CA 了之後,我們可以來檢查一下憑證是不是如我們預期的建立了:
$ openssl x509 -noout -text -in ca.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 13178084018309537468 (0xb6e1f5981b979ebc)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=TW, ST=Taiwan, O=Davy.TW, OU=Davy.TW Certificate Authority, CN=Davy.TW Root CA
Validity
Not Before: Nov 28 17:06:10 2019 GMT
Not After : Nov 23 17:06:10 2039 GMT
Subject: C=TW, ST=Taiwan, O=Davy.TW, OU=Davy.TW Certificate Authority, CN=Davy.TW Root CA
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (4096 bit)
Modulus: ...
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
DC:74:06:33:97:F5:E6:58:2E:51:66:14:81:B4:4F:EF:4C:78:9B:9B
X509v3 Authority Key Identifier:
keyid:DC:74:06:33:97:F5:E6:58:2E:51:66:14:81:B4:4F:EF:4C:78:9B:9B
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
...
$
我們可以看到以下資訊:
Signature Algorithm
: 簽章演算法Validity
: 憑證有效期限Public-Key
長度Issuer
: 簽發單位,這邊應該會顯示我們剛剛填寫的內容Subject
: 憑證主體,因為我們是自簽 Root CA,所以應該會跟 Issuer 相同
另外,因為我們有使用 v3_ca
的擴充,所以還會看到這個憑證帶有 X509v3 擴充資訊,並顯示 X509v3 Basic Constraints
中有 CA:TRUE
,表示這張憑證當作 CA 使用。
簽發 Intermediate CA
在有了 Root CA 之後,我們就可以利用這個 Root CA 在底下簽發 Intermediate CA,接下來會稍稍有點複雜,因為預設的 openssl.cnf
通常都不會帶有適用的現成 extension 可以用,我們必須自訂這些東西:
建立 Intermediate CA Key
我們假設 Intermediate CA 與 Root CA 的單位不同,所以我們需要使用不同的 Key 來簽名,一樣,使用 openssl genrsa
簡單地建立 Key:
$ openssl genrsa -aes256 -out intermediate.key 4096
Generating RSA private key, 4096 bit long modulus
# ...
e is 65537 (0x10001)
Enter pass phrase for intermediate.key:
Verifying - Enter pass phrase for intermediate.key:
$
建立 Intermediate CA CSR
既然兩個 CA 的單位不同,總不可能讓 Intermediate CA 用的 Key 流出去到 Root CA 那邊吧?
所以我們要先建立 CSR(Certificate Signing Request),再將 CSR 交給 Root CA 來核發憑證:
$ openssl req -sha256 -new -key intermediate.key -out intermediate.csr
Enter pass phrase for intermediate.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:TW
State or Province Name (full name) [Some-State]:Taiwan
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Davy.TW
Organizational Unit Name (eg, section) []:Davy.TW Blog Certificate Authority
Common Name (e.g. server FQDN or YOUR name) []:Davy.TW Blog Intermediate CA
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
$
CSR 會記錄憑證要記載的 Subject 事項,因此我們剛剛輸入的內容等下都會變成憑證的一部分。
簽發 Intermediate CA
在將 CSR 交給 Root CA 的單位之後,我們就可以準備簽發 Intermediate CA,這邊我們先定義一個給 Intermediate CA 用的 extension:
$ cat intermediate.ext
[ intermediate_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:true, pathlen:0
$
為什麼要自己定義呢? 用原本的 v3_ca
不好嗎?
當然,如果你想要讓這個 CA 跟 Root CA 一樣可以繼續往下簽 Intermediate CA 的話,那你可以選擇繼續使用 v3_ca
。
上面定義的 intermediate_ca
相較預設的 v3_ca
多了一個 pathlen:0
,表示他往下還可以有 0 個 Intermediate CA(也就是沒有的意思)。
$ openssl x509 -req -in intermediate.csr \
-CA ca.pem -CAkey ca.key \
-CAserial ca.serial -CAcreateserial \
-days 730 \
-extensions intermediate_ca -extfile intermediate.ext \
-out intermediate.pem
Signature ok
subject=/C=TW/ST=Taiwan/O=Davy.TW/OU=Davy.TW Blog Certificate Authority/CN=Davy.TW Blog Intermediate CA
Getting CA Private Key
Enter pass phrase for ca.key:
$
其中有幾個選項需要介紹一下:
-CAserial ca.serial
: 每個 CA 簽發憑證時都需要一個流水號,這裡是指出流水號記錄檔檔名-CAcreateserial
: 只有當 CA 初次簽發時需要,他會初始化一個流水號出來,第二次之後簽發就不要帶這個參數,以免流水號被重置-extensions intermediate_ca
: 我們想要指定剛剛我們建立的 extension-extfile intermediate.ext
: 需要指定包含 extension 的設定檔
驗證 Intermediate CA
一樣,首先先看一下憑證內容是不是正確的:
$ openssl x509 -noout -text -in intermediate.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 15568167468282800826 (0xd80d3dd426c9eaba)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=TW, ST=Taiwan, O=Davy.TW, OU=Davy.TW Certificate Authority, CN=Davy.TW Root CA
Validity
Not Before: Nov 29 10:09:13 2019 GMT
Not After : Nov 28 10:09:13 2021 GMT
Subject: C=TW, ST=Taiwan, O=Davy.TW, OU=Davy.TW Blog Certificate Authority, CN=Davy.TW Blog Intermediate CA
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (4096 bit)
Modulus: ...
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
EA:0D:FA:82:1B:80:BA:00:5A:0D:37:7B:DB:52:71:85:E0:84:3F:13
X509v3 Authority Key Identifier:
keyid:DC:74:06:33:97:F5:E6:58:2E:51:66:14:81:B4:4F:EF:4C:78:9B:9B
X509v3 Basic Constraints:
CA:TRUE, pathlen:0
Signature Algorithm: sha256WithRSAEncryption
...
$
可以看到 Issuer
是我們 Root CA 的名稱,而 Subject
是 CSR 中填寫的內容。
並且有將 pathlen
設為 0
,表示這個 CA 只能用來簽發終端憑證。
另外,我們還可以透過 openssl verify
驗證這個憑證是不是由 Root CA 一路發下來的。
$ openssl verify -CAfile ca.pem intermediate.pem
intermediate.pem: OK
簽發終端憑證
好的,既然我們有了中介 CA,那我們就可以來試試看簽發終端憑證並驗證是不是有成功形成一個 chain:
建立終端憑證 Key/CSR
這部份就不累述,一樣的步驟:
$ openssl genrsa -aes256 -out endentity.key 4096
Generating RSA private key, 4096 bit long modulus
...
e is 65537 (0x10001)
Enter pass phrase for endentity.key:
Verifying - Enter pass phrase for endentity.key:
$ openssl req -sha256 -new -key endentity.key -out endentity.csr
Enter pass phrase for endentity.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:TW
State or Province Name (full name) [Some-State]:Taiwan
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Davy.TW
Organizational Unit Name (eg, section) []:Davy.TW Blog
Common Name (e.g. server FQDN or YOUR name) []:blog.davy.tw
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
$
這邊要注意的是,由於我們的終端憑證會使用在服務上,所以 Common Name
的部分要記得填寫服務的 FQDN (domain name)。
簽發終端憑證
由於我們只是要簽發終端憑證而不是其他特殊用途的憑證,所以就不需要再另外定義 extension:
$ openssl x509 -req -in endentity.csr \
-CA intermediate.pem -CAkey intermediate.key \
-CAserial intermediate.serial -CAcreateserial \
-days 365 \
-out endentity.pem
Signature ok
subject=/C=TW/ST=Taiwan/O=Davy.TW/OU=Davy.TW Blog/CN=blog.davy.tw
Getting CA Private Key
Enter pass phrase for intermediate.key:
$
驗證終端憑證
$ openssl x509 -noout -text -in endentity.pem
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 9266986341589110827 (0x809af2faa4d0d02b)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=TW, ST=Taiwan, O=Davy.TW, OU=Davy.TW Blog Certificate Authority, CN=Davy.TW Blog Intermediate CA
Validity
Not Before: Nov 30 06:29:00 2019 GMT
Not After : Nov 29 06:29:00 2020 GMT
Subject: C=TW, ST=Taiwan, O=Davy.TW, OU=Davy.TW Blog, CN=blog.davy.tw
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (4096 bit)
Modulus:
...
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
...
$ openssl verify -CAfile ca.pem -untrusted intermediate.pem endentity.pem
endentity.pem: OK
$
OpenSSL 會在 verify 的時候先驗證 untrusted 憑證的正確性,然後如果會拿來驗證後面的憑證,也就是說如果結果是 OK 的話就表示這些憑證在一個有效的 chain 上了。
題外:CA bundle
大家記得在使用憑證的時候要建立 CA bundle,方法也很簡單,就是把這些 CA 憑證全部接在一起就可以了:
$ cat intermediate.pem ca.pem > ca-bundle.pem
$ openssl verify -CAfile ca-bundle.pem endentity.pem
endentity.pem: OK
$
至於為什麼不在驗證的時候就直接拿 CA bundle 來驗證,背後原因有點複雜,可以看這個 mailing list 的說明: https://mail.python.org/pipermail/cryptography-dev/2016-August/000676.html
後記
其實憑證真的有夠麻煩…… 裡面還有很多東西可以研究,光是 x509 extension 就可以啃很久……
希望大家都可以在需要簽中介憑證的時候都可以簡單做完XD
Let's Encrypt 是由非營利性互聯網安全研究組(ISRG)提供給您的免費,自動化和開放的憑證頒發機構。 詳情請見: https://letsencrypt.org/zh-tw/ ↩︎