我們在登錄到 SSH 服務器的時候,都比較喜歡使用更安全的公鑰驗證,配合 ssh-agent 的話還可以免去每次都輸入密碼的麻煩。可能很多人都不知道的是, GPG 生成的密鑰可以導出爲 SSH 的格式,直接用於 SSH 登錄驗證。生成密鑰 的一般步驟在 另一篇日誌 中有詳細說明,所以這裡只專注於「不一般」 的部分。

生成認證密鑰

並不是所有的 GPG 密鑰都可以被用來做 SSH 公鑰驗證的。 GPG (子)密鑰有 不同的用途(usage/capability),只有具有 authenticate 用途的密鑰才可以 被用來登錄到 SSH 服務器。

我們通過一般途徑生成的 GPG 密鑰並沒有 authenticate 用途,最簡單的方法 就是在普通密鑰上執行 GPG 的 addkey 命令添加一個用於 SSH 認證的子密 鑰。首先我們進入「專家模式」的 GPG 命令行:

1
gpg2 --expert --edit-key '[email protected]'

在命令行中輸入 addkey ,程序會詢問密鑰類型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
gpg> addkey
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
Your selection? 8

我們選擇選項「8」,兼容性很好的 RSA 算法。接下來就要確定這個密鑰的用途:

1
2
3
4
5
6
7
8
9
Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt

   (S) Toggle the sign capability
   (E) Toggle the encrypt capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection?

我們需要一個能進行 authenticate 操作的密鑰,所以依次選擇「S」,「E」, 「A」選項,關閉「Sign」和「Encrypt」,啓用「Authenticate」. 最後選擇 「Q」保存這個子密鑰的用途。

接下來的操作與一般的密鑰配置類似,在成功生成子密鑰後別忘了輸入 save 命令保存更改。

現在來羅列一下我們的密鑰:

1
gpg2 --list-key '[email protected]' --with-keygrip

輸出的密鑰列表應該類似這樣:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
pub   ed25519 2016-10-10 [C]
      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      Keygrip = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
uid           [ultimate] Your Name <[email protected]>
sub   rsa4096 2016-10-10 [S] [expires: 2020-10-10]
      Keygrip = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
sub   rsa4096 2016-10-10 [E] [expires: 2020-10-10]
      Keygrip = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
sub   rsa4096 2016-10-10 [A] [expires: 2020-10-10]
      Keygrip = GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG

密鑰創建時間「2016-10-10」後面方括號中的字母說明了這個密鑰的用途: 「C」==「Certify」,「S」==「Sign」,「E」==「Encrypt」, 「A」==「Authenticate」. 位於列表最後帶字母「A」的就是我們剛剛生成 的用於 SSH 認證的子密鑰, GGGGGGGG... 是這個子密鑰的標識,後面 配置 gpg-agent 的時候會用到。

執行以下命令,就可以得到我們的 SSH 公鑰了:

1
gpg2 --export-ssh-key '[email protected]'

這個公鑰的使用方法與一般的 SSH 公鑰一樣,請添加到目標主機的 $HOME/.ssh/authorized_keys 文件中。

配置 gpg-agent

Gpg-agent 與 ssh-agent 類似,是一個緩存 GPG 密鑰的守護程序,而且它 可以代替 ssh-agent 向 SSH 客戶端提供驗證用的密鑰,只需要在啓動時指 定 --enable-ssh-support 選項即可。

爲了日常使用方便,我們最好自動化 gpg-agent 的啓動操作。我使用 zsh , 所以自製了一個 啓動腳本 ,在進入 shell 時如果 gpg-agent 還沒啓動 則啓動之。

另外我們還需要向 gpg-agent 說明哪些密鑰可以被用來做 SSH 認證。前面 我們剛生成的子密鑰有一個 40 位十六進制數字組成的 keygrip (在例子中 以 GGGGGGGG... 代替),將其填入 $HOME/.gnupg/sshcontrol 文 件中就大功告成了。

現在,當我們的 SSH 客戶端進行公鑰認證時,就會轉而向 gpg-agent 索要 GPG 密鑰。確認新的 GPG 密鑰和 gpg-agent 能正確工作後,我們就可以扔 掉舊的 SSH 密鑰了。