🐔

ssh の既存の秘密鍵を gpg にインポートする

2021/08/10に公開

背景

Windows の SSH で gpg-agent を使う」で SSH で gpg-agent を使うことが出来るようになったのだが、そのままだと今まで使っていた秘密鍵が使えない。
まぁサーバの authorized_keys を片っ端から書き替えていけばいい話ではあるのだが、ちょいと面倒だし何よりそれだと負けた気がするので(?)何とかして既存の秘密鍵を GPG にインポートしたくなったのだが、試行錯誤ののちうまくサブキーとしてインポートすることができたので、忘備のためにメモを残しておく。

GPG への SSH 秘密鍵のインポート(元が PuTTY 秘密鍵の場合)

まずは元が PuTTY 秘密鍵の場合、つまり、拡張子が .ppk の場合であるが、とりあえず以下の記事で PuTTY と gpg-agent が連携できている状態とする。
https://zenn.dev/kariya_mitsuru/articles/b7da14c9aa5171
もちろん、この記事にある認証用のサブキーの追加は必要ない。(していても問題ない)

インポートしたい秘密鍵のファイルをダブルクリックすると pageant が起動して、秘密鍵にパスワードが設定されていれば(普通してあるよね?)以下のようなダイアログ表示されてパスワードを要求されると思う。

もちろん、.ppkpageant に関連付けてなければ pageant が起動すらしないが、その場合はエクスプローラで pageant の実行ファイルを表示してからインポートしたい秘密鍵をそいつにドラッグアンドドロップすれば良い。

ここで正しいパスワードを入力すると(パスワードを設定していなければ入力無しで)、次に以下のようなダイアログが表示されて GPG にインポートした後のパスフレーズを要求される。

ここでパスフレーズを設定すれば、以降 PuTTY や SSH 等 gpg-agent に連携した SSH クライアントであれば gpg-agent 経由で当該秘密鍵が使用できるようになる。
なお、ここで設定するパスフレーズはプライマリキーのパスフレーズと同じモノを設定しておけば後々面倒な事にならないで済む。

これで PuTTY 秘密鍵のインポートは完了だ。
ちなみに、%APPDATA%\gnupg\sshcontrol への追記は自動で行われている。

GPG への SSH 秘密鍵のインポート(元が OpenSSH 秘密鍵の場合)

元が OpenSSH 秘密鍵の場合であるが、こちらも以下の記事で SSH と gpg-agent が連携できている状態とする。
https://zenn.dev/kariya_mitsuru/articles/b7da14c9aa5171
もちろん、この記事にある認証用のサブキーの追加は必要ない。(していても問題ない)

ちなみに、記事では Windows 標準の SSH との連携しか書いていないが、神記事「混沌を極めるWindowsのssh-agent事情」を参考に gpg-agent との連携さえできていれば他の SSH でも問題なく秘密鍵のインポートができる。
更に言えば、SSH と gpg-agent の連携さえできていれば Windows 以外でも基本的な手順は同じである。

さて、PuTTY 秘密鍵の場合と同様まずは既存の秘密鍵を gpg-agent 経由で取り込むのだが、SSH 秘密鍵の場合は ssh-add コマンドを使用する。引数は SSH 秘密鍵ファイルのファイル名だ。

C:\Users\kariy>ssh-add id_rsa
Enter passphrase for id_rsa:

ここで SSH 秘密鍵の正しいパスワードを入力すると(パスワードを設定していなければ入力無しで)、次に以下のようなダイアログが表示されて GPG にインポートした後のパスフレーズを要求される。

ここでパスフレーズを設定すれば、以降 PuTTY や SSH 等 gpg-agent に連携した SSH クライアントであれば gpg-agent 経由で当該秘密鍵が使用できるようになる。
なお、ここで設定するパスフレーズはプライマリキーのパスフレーズと同じモノを設定しておけば後々面倒な事にならないで済む。

入力が済むと以下のような表示が出て OpenSSH 秘密鍵のインポートは完了だ。

Identity added: id_rsa (id_rsa)

ちなみに、%APPDATA%\gnupg\sshcontrol への追記は自動で行われている。

ちょいと余談

上記では PuTTY の秘密鍵は pageant 経由で、OpenSSH の秘密鍵は ssh-add 経由でインポートしているが、PuTTYgen を使えばこれらを相互に変換できるので、実際の所は両方の手順は必要ではなく、どちらかの手順だけでも実現は可能だ。

分かっていて敢えて両方の手順を書いたのは、何となく Windows なら pageant だろうし Linux なら ssh-add だろうと思ったからだ。

インポートした秘密鍵のサブキー化

さて、この状態では GPG に当該秘密鍵はインポートされた状態ではあるものの、この秘密鍵は Kleopatra でも表示されないし、秘密鍵一覧を表示するコマンドである gpg -K でも表示されない。
つまり、この状態では当該秘密鍵は GPG のプライマリキー、あるいはサブキーとして認識されているわけではなく、あくまでも gpg-agent を経由して PuTTY やその他の SSH で使用できる状態になっているだけである。

もちろんこのままでも SSH の秘密鍵として使用する分には動作に支障があるわけではない。が、やっぱりこのままではまだ負けた気がするので(?)、ここから当該秘密鍵を GPG のプライマリキーのサブキーにする。

まず現在 GPG の正規の秘密鍵(?)として認識されているキーの keygripgpg -K --with-keygrip コマンドで確認する。

C:\Users\kariy>gpg -K --with-keygrip
C:/Users/kariy/AppData/Roaming/gnupg/pubring.kbx
------------------------------------------------
sec   ed25519 2021-08-08 [C]
      5D5A946A0C5848853742A0CC37C6E17F714D3549
      Keygrip = 71A9E9451BD11D9E5C01BCB18F8110565636A160
uid           [ultimate] Mitsuru Kariya <kariya_mitsuru@hotmail.com>
ssb   ed25519 2021-08-08 [A]
      Keygrip = E8C3EFAA0F2CBBD8CDF78FE7D03567AD45CA237D

上記では、先頭が sec で役割に [C] の付いている 71A9E9451BD11D9E5C01BCB18F8110565636A160 がプライマリキーの keygrip、先頭が ssb で役割に [A] の付いている E8C3EFAA0F2CBBD8CDF78FE7D03567AD45CA237D が GPG で生成した認証用キーの keygrip である。(記事「Windows の SSH で gpg-agent を使う」を実施した直後の状態)
見ての通り、インポートした SSH 秘密鍵はやはり表示されない。

続いて、gpg-agent を経由してインポートした秘密鍵の keygrip を確認する。
エクスプローラで %APPDATA%\gnupg\private-keys-v1.d を開くといくつかのファイルがあるのが分かると思う。

これらのファイルは、実はファイル名が keygrip で拡張子が .key の秘密鍵である。つまり、こいつらが GPG に取り込まれている秘密鍵の実体だ。
ちなみに、Linux でのパスは通常 ~/.gnupg/.private-keys-v1.d なので、その中を見れば同じような事になっている。

で、鋭い方は既に気付いているかもしれないが、先程のコマンドで確認した keygrip にはいない keygrip をファイル名に持っているファイルがあるのが分かる。上記の場合は 8E127A85DB446D0721048E3E25FA6684029A3EA1 である。
つまり、こいつが gpg-agent 経由でインポートした秘密鍵の keygrip である。

ちなみに、ファイルの更新時刻を見れば一発で分かるという話もあるが、ここでは念のため gpg -K --with-keygrip の結果と突合した。

さて、サブキーにしたいキーの keygrip が分かってしまえば実は後の作業は簡単である。
まず記事「Windows の SSH で gpg-agent を使う」でサブキーを生成する際にも使った gpg --expert --edit-key コマンドを実行する。引数はユーザ ID(プライマリキー生成の時に指定した「名前」)だ。

C:\Users\kariy>gpg --expert --edit-key "Mitsuru Kariya"
gpg (GnuPG) 2.2.28; Copyright (C) 2021 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  ed25519/37C6E17F714D3549
     created: 2021-08-08  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  ed25519/DADC6910610315D7
     created: 2021-08-08  expires: never       usage: A
[ultimate] (1). Mitsuru Kariya <xxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxx>

gpg> 

なお、実は引数はユーザ ID じゃなくて、Key ID(上記の 37C6E17F714D3549)や指紋(fingerprint、上記のモノは 5D5A946A0C5848853742A0CC37C6E17F714D3549)でも良い。

話がそれた。続いて addkey コマンドを叩くとやりたいことを選べと言われるので今回は既存のキーの追加である 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
  (14) Existing key from card
Your selection? 13
Enter the keygrip:

するとこちらの思惑通りに keygrip を聞いてくるので、先程特定した秘密鍵の keygrip を入力する。

Enter the keygrip: 8E127A85DB446D0721048E3E25FA6684029A3EA1

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 だけにして進める(別に Authenticate 以外を残しても良いが)。ちなみに、今回インポートしているキーは RSA なのでデフォルトの役割が Sign(署名)と Encrypt(暗号化)の両方になっている。

Your selection? s

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Encrypt

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

Your selection? e

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions:

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

Your selection? a

Possible actions for a RSA key: Sign Encrypt Authenticate
Current allowed actions: Authenticate

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

Your selection? q
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y

で、最後にプライマリキーのパスフレーズを聞かれるので入力すると、めでたく追加される。

sec  ed25519/37C6E17F714D3549
     created: 2021-08-08  expires: never       usage: C
     trust: ultimate      validity: ultimate
ssb  ed25519/DADC6910610315D7
     created: 2021-08-08  expires: never       usage: A
ssb  rsa2048/6AD23C966477DE8E
     created: 2021-08-09  expires: never       usage: A
[ultimate] (1). Mitsuru Kariya <kariya_mitsuru@hotmail.com>

ちなみに、なぜかプライマリキーのパスフレーズを 2 回聞かれる事もあるので、その場合はキレずにちゃんと入力しよう。
最後に quitgpg から抜けるのだが、この場合にもサブキーの生成と同様に「保存する?」と聞かれるのでちゃんと y を答えて保存しよう。

gpg> quit
Save changes? (y/N) y

これで、晴れて gpg-agent 経由でインポートした SSH 秘密鍵が GPG のサブキーになった。

気になる場合には Kleopatra や gpg コマンドでサブキーを確認してみよう。確認の仕方は「Windows の SSH で gpg-agent を使う・サブキーペアの確認」を参照のこと。

なお、上記でインポートしている既存の秘密鍵は RSA だったが、もちろん ed25519 のキーも同様の手順でインポートできる。

終わりに

実は既存の SSH 秘密鍵を GPG にサブキーとしてインポートする方法は散々インターネットで調べてもなかなか見つからず、唯一見つけたのが以下の記事だったのだが、こちらの方法では monkeysphere なるソフトウェアを使って手順も結構手間な上、ed25519 がサポートされていなかったので、半ば諦めていた。
https://opensource.com/article/19/4/gpg-subkeys-ssh-multiples

しかし、サブキーとしてインポートするのを諦めて gpg-agent を単に ssh-agentpageant の替わりとして使うだけであればいろいろな記事が見つかったため、それらの手順を試した際にふと %APPDATA%\gnupg\private-keys-v1.d を覗いて見たらそれっぽいファイルが出来ているのに気づいた。
そこで、上記の記事の手順で既存キーの追加を keygrip を指定して実施していることを思い出して、ダメもとで gpg からは見えない keygrip を指定したら行けた。

と言うわけで、とりあえず当初の目的は達成したが、実はもっとスマートな方法があるのかもしれない。
もしご存じの方がいたら是非教えて欲しい。

Discussion