如何從 NSSDB (certutil) 中取出 pem 格式的 key

本篇提到的 certutil 系指 UNIX 系統下的 Certificate Database 管理工具,與 Windows Server 的 certutil 無關,若想了解 Windows Server 下的 certutil,請造訪: https://docs.microsoft.com/zh-tw/windows-server/administration/windows-commands/certutil

因為最近在玩 FreeIPA 的關係,會需要幫 https 服務簽署憑證,在理解如何使用 FreeIPA 簽署的同時,順便發現到了一個管理憑證與其金鑰的工具 —— certutil[1]

certutil 可以管理 NSS Database 裡面的憑證與金鑰,FreeIPA 中的 PKI 服務 Dogtag 亦使用此一工具來管理憑證,就連 Firefox 自有的憑證庫也是使用這個方式儲存。不過,雖然這個 toolset 很棒,但我的 https 服務只吃 pem 格式的憑證,但從 NSSDB 取出的金鑰則會是 p12[2] 格式,這裡需要再做一些人工的轉換,所以這裡筆記一下該如何使用這些工具順便簡單介紹一下從 FreeIPA 上簽發憑證的流程吧。

建立 NSSDB 並放入 CA

在跟 FreeIPA 請求憑證簽署之前,我們要先有自己的金鑰庫,所以我們要先透過 certutil 來建立金鑰庫並先匯入我們的 CA 憑證,先假設我們要把 NSSDB 存放在 ~/certs

$ mkdir -p ~/certs
$ certutil -N -d ~/certs # 建立新的 NSSDB
$ certutil -A -d ~/certs -n 'IPA CA' -t CT,, -a < ca.crt # 匯入 CA 憑證

certutil 的指令說明可以去參考使用手冊,這裡只會解釋有用到的部分:

  • -d 指定 NSSDB 位置
  • -N 建立新的 NSSDB
  • -A 匯入已存在的憑證
    • -n 給予該憑證一個暱稱(此例為 IPA CA
    • -t 設定該憑證的信任模式,這裡我們採取信任該 CA 簽署的任何 Server/Client SSL
    • -a 採 ASCII 編碼輸入/出

建立金鑰及憑證簽署請求

有了自己的金鑰庫之後,就是要產生金鑰及憑證簽署請求(Certificate Request)了,透過 certutil 我們可以一步同時產生兩者:

$ certutil -R -d ~/certs -a -g 4096 -s CN=web.example.com,O=EXAMPLE.COM > web.csr
  • -R 產生一個憑證簽署請求檔(.csr
    • -g 金鑰長度,預設為 1024(此例為 4096
    • -s 主體名稱(Subject),此處需填寫此憑證的 Common Name(通常是 FQDN),以及 FreeIPA 要求需填寫 Organization

若需要加入 SAN[3],可以再加上 --extSAN 參數,例如 --extSAN dns:web.example.com,dns:web.example.org

從 FreeIPA 簽發憑證

從 FreeIPA 簽發的部分可以從 Web UI 或 CLI 建立,這邊兩者都會介紹,請讀者自己選擇自己喜歡的方式:

via CLI

$ kinit admin # 先登入有權限簽發憑證的 IPA 帳號
Password for [email protected]:
$ ipa cert-request --principal=HTTP/web.example.com web.csr
Issuing CA: ipa
Certificate: ...
Subject: CN=web.example.com,O=EXAMPLE.COM
Issuer: CN=Certificate Authority,O=EXAMPLE.COM
Not Before: Wed Mar 18 00:00:00 2020 UTC
Not After: Thu Mar 17 00:00:00 2022 UTC
Serial number: 11 # <= 記下序號
Serial number (hex): 0xB
$ ipa cert-show 11 --out=web.crt # 填入序號(11)
Issuing CA: ipa
Certificate: ...
Subject: CN=web.example.com,O=EXAMPLE.COM
Issuer: CN=Certificate Authority,O=EXAMPLE.COM
Not Before: Wed Mar 18 00:00:00 2020 UTC
Not After: Sat Mar 19 00:00:00 2022 UTC
Serial number: 11
Serial number (hex): 0xB
Revoked: False
  • ipa cert-request 簽署憑證
    • --principal IPA 中的 Kerberos 主體
  • ipa cert-show 取得對應序號之憑證

取得憑證後,我們接下來就是要準備取得 pem 格式的金鑰了。

via Web UI

首先找到我們要簽署的服務(HTTP/web.example.com),然後按下 Service Certificate 中的 Add,並填入剛剛產生的 csr 內容:


接下來對剛剛產生的 Certificate 按下 Actions -> Get,就可以取得剛剛產生的憑證,我們先複製下來並儲存到 web.crt,然後就可以準備取得 pem 格式的金鑰了。


取得 pem 格式金鑰

終於要進入本篇主題了 —— 取得 NSSDB 中的金鑰,這裡我們還會需要 NSS Security Tools 裡的另一個工具 pk12util[4],他負責將 NSSDB 中的憑證及金鑰以 p12 的格式匯入/匯出,我們稍後要利用他匯出 p12 格式的金鑰並轉成 pem 格式。

匯入簽發憑證

在取得金鑰之前,我們必須先匯入剛剛取得的憑證,讓 NSSDB 知道我們有一個憑證對應到剛剛產生的金鑰,之後我們才可以將金鑰取出:

$ certutil -A -d ~/certs -n web -t u,u,u -i web.crt
  • -A 匯入已存在的憑證
    • -n 給予該憑證一個暱稱(此例為 web
    • -t 設定該憑證的信任模式,這裡我們信任該憑證作為驗證及簽章用
    • -i 憑證檔名

取出 p12 金鑰

成功匯入憑證之後我們就可以使用 pk12util 將金鑰匯出了:

$ pk12util -d ~/certs -n web -o web.p12

pk12util 的參數大致上都跟 certutil 差不多,執行完畢後會得到一個 p12 格式的金鑰 web.p12

轉換成 pem 金鑰

轉換的步驟也很簡單,透過萬能的 OpenSSL 就可以轉換了:

$ openssl pkcs12 -in web.p12 -out web.key -nocerts -nodes

經過轉換後的 pem 格式金鑰會被存放在 web.key 中,如果打開來看的話會發現除了金鑰本體,還會附上一些額外資訊如下,如果是要拿去給服務吃的話可以直接刪掉留下金鑰本體即可:

Tag Attributes
    friendlyName: web
    localKeyID: ...
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----

後記

之前有發過一篇關於使用 OpenSSL 來簽發中介 CA 的方法,這次改嘗試使用不同的工具來操作憑證,畢竟達到相同目的的工具百百種,多用一些抓到自己最順手的方式及工具可以大大的提升工作效率,如果有興趣使用 NSSDB 管理憑證及金鑰的朋友們可以多試試看,真的滿方便的,金鑰再也不會亂丟了 XDDDD


  1. 一個在 UNIX 系統下管理 Certificate Database 的工具,屬於 NSS Security Tools 的一部分,在 Debian/Ubuntu 下套件名為 libnss3-tools;RHEL/Fedora/CentOS 下則為 nss-tools。使用說明可以參考 MDNman 1 certutil↩︎

  2. PKCS #12,存放金鑰及憑證的一種格式,常見副檔名有 .p12.pfx,詳見維基百科上的《PKCS 12》條目 ↩︎

  3. Subject Alternative Name,為 X.509 extension 的一部分,支援單一憑證擴充描述替代用的主體名稱,可用來讓憑證支援多個 domain。 ↩︎

  4. 使用說明可以參考 MDNman 1 pk12util↩︎

因主題更新,留言功能暫時停用中。