keytocardでGPGの秘密副鍵をYubiKeyへ登録すると、操作したGnuPG環境では秘密副鍵の実体がカード参照用の情報へ置き換わる。
PCにも秘密副鍵を残すには、一時的なGNUPGHOMEへ秘密副鍵を複製し、その環境でkeytocardを実行すればよい。
作業後の鍵構成
作業前のPCには、公開主鍵と、署名、認証、暗号化に使う三つの秘密副鍵がある。 秘密主鍵はオフラインのバックアップだけに保存している。
作業後もPCの秘密副鍵は変わらず、YubiKeyにも同じ副鍵が入る。
オフラインバックアップ
└─ 秘密主鍵
PC
├─ 署名用の秘密副鍵
├─ 認証用の秘密副鍵
└─ 暗号化用の秘密副鍵
YubiKey OpenPGP
├─ Signature slot 署名用の秘密副鍵
├─ Authentication slot 認証用の秘密副鍵
└─ Encryption slot 暗号化用の秘密副鍵
これは、PC上の秘密副鍵を失わずにYubiKeyの利用を始めるための構成である。
keytocardが変更するもの
GnuPGの公式マニュアルは、keytocardを選択した秘密鍵をスマートカードへ転送するコマンドとして説明している。
転送に成功してsaveすると、操作中のGnuPG環境では秘密鍵の実体がカード参照用stubへ置き換わる。
このstubはカードのシリアル番号などを保持し、秘密鍵を使うときに対応するカードを要求する。
したがって、普段使っている~/.gnupgでkeytocardを実行すると、その環境にある秘密副鍵の実体がstubへ置き換わる。
一時的なGNUPGHOMEで実行すれば、一時環境の秘密副鍵だけがstubへ置き換わり、通常環境の秘密副鍵は残る。
GnuPG 2.1以降では、秘密鍵とstubをgpg-agentがprivate-keys-v1.d以下で管理する。
秘密鍵の実体が従来の秘密鍵リングだけに入っている、という説明は現在のGnuPG 2.xには当てはまらない。
検証対象の環境
この記事では、次の環境を前提にする。
OS: Ubuntu 24.04.4 LTS
GnuPG: 2.4.4
YubiKey: YubiKey 5C NFC
YubiKey firmware: 5.7.4
OpenPGP application: 3.4
署名副鍵: Ed25519
認証副鍵: Ed25519
暗号化副鍵: Curve25519
YubiKey 5のOpenPGPアプリケーションが対応するアルゴリズムは、ファームウェアや製品系列によって異なる。 Yubicoの資料では、Ed25519とX25519を含む楕円曲線暗号はファームウェア5.2.3以降でサポートされている。
GnuPG 2.4.0にあったYubiKey向けkeytocardの不具合は2.4.1で修正されている。
別のバージョンで作業する場合は、そのバージョンのリリースノートも確認する。
鍵とカードの事前確認
最初に、GnuPGのバージョンと現在の秘密鍵を確認する。
gpg --version
gpg --list-secret-keys \
--keyid-format LONG \
--with-subkey-fingerprint
対象の鍵は、短いキーIDではなく公開主鍵の完全なフィンガープリントで指定する。 次の変数に、空白を除いたフィンガープリントを設定する。
PRIMARY_FINGERPRINT="ここに公開主鍵の完全なフィンガープリント"
秘密鍵一覧の先頭に表示される記号にも意味がある。
sec、ssb:利用できる秘密鍵の実体があるsec#、ssb#:対応する秘密鍵の実体がないsec>、ssb>:秘密鍵がスマートカード上にある
作業前に、秘密主鍵がsec#、三つの秘密副鍵がssbと表示されることを確認する。
YubiKeyも接続して状態を確認する。
gpg --card-status
次の項目を確認する。
- OpenPGPアプリケーションとして認識されていること
Key attributesが登録する鍵のアルゴリズムと一致していることSignature key、Encryption key、Authentication keyのフィンガープリント- 初期値ではないUser PINとAdmin PINを設定済みであること
カード側とPC側のフィンガープリントを用途ごとに比較し、次のいずれかを選ぶ。
- 三つのスロットがすべて空:一時環境を作り、登録作業へ進む
- 三つのスロットがPC側の副鍵とすべて一致:目的の二重保持は完了しているため、以降の登録作業は不要
- 別の鍵がある、または一部だけ一致する:上書きせず、カードの用途とバックアップを確認する
既存の鍵が入っているスロットを上書きすると、その秘密鍵をYubiKeyから取り戻せない。
同じフィンガープリントがすでに表示されている場合も、keytocardをやり直す理由にはならない。
秘密主鍵のバックアップがあっても、現在の秘密副鍵まで含まれているとは限らない。 YubiKeyは秘密鍵のバックアップ先にならないため、S、A、Eの各秘密副鍵を復元できるオフラインバックアップも先に確認しておく。
一時的なGnuPG環境を作る
一時ディレクトリはmktempで作成し、所有者以外が読み書きできないようにする。
umask 077
TEMP_GNUPGHOME="$(mktemp -d)"
chmod 700 "$TEMP_GNUPGHOME"
printf '%s\n' "$TEMP_GNUPGHOME"
GNUPGHOME環境変数は変更せず、各コマンドへ--homedir "$TEMP_GNUPGHOME"を渡して通常環境との取り違えを防ぐ。
秘密副鍵を一時環境へ複製する
通常環境から秘密副鍵をエクスポートし、そのまま一時環境のGnuPGへ渡す。
gpg --export-secret-subkeys "$PRIMARY_FINGERPRINT" |
gpg --homedir "$TEMP_GNUPGHOME" --import
--export-secret-subkeysは、「指定した副鍵だけを単独で出力する」オプションではない。
公開主鍵を含む鍵ブロックを出力し、秘密主鍵の部分だけを利用不能なダミーに置き換える。
秘密副鍵の実体は含まれるため、出力を安全でない場所へ保存すると秘密副鍵が漏洩する。
この手順はパイプで直接インポートするため、秘密鍵を含む中間ファイルを作らない。 公開部分も含まれるので、公開鍵の別途インポートは不要である。
インポート後の状態を確認する。
gpg --homedir "$TEMP_GNUPGHOME" \
--list-secret-keys \
--keyid-format LONG \
--with-subkey-fingerprint
秘密主鍵がsec#、署名、認証、暗号化副鍵がssbと表示されることを確認する。
対象ではない鍵が表示された場合や、必要な副鍵に#または>が付いている場合は、keytocardへ進まない。
YubiKeyの鍵属性を揃える
YubiKeyの各スロットには、格納する鍵のアルゴリズムを表す鍵属性がある。
gpg --card-statusで次の表示を確認できた場合、この節の操作は不要である。
Key attributes ...: ed25519 cv25519 ed25519
スロットが空で、鍵属性が異なる場合だけカードを編集する。
gpg --homedir "$TEMP_GNUPGHOME" --edit-card
管理者向けのコマンドを有効にして、key-attrを実行する。
gpg/card> admin
gpg/card> key-attr
Signature、Encryption、Authenticationの順に設定を求められる。 三つとも鍵の種類にECC、曲線にCurve 25519を選ぶ。
Signature key: ed25519
Encryption key: cv25519
Authentication key: ed25519
設定を終えたら、カード編集を終了する。
gpg/card> quit
終了後にカードの状態を表示する。
gpg --homedir "$TEMP_GNUPGHOME" --card-status
Key attributesがed25519 cv25519 ed25519になったことを確認する。
鍵属性の変更もYubiKey本体への書き込みなので、各スロットが空の場合に限って実行する。
YubiKeyへ秘密副鍵を登録する
一時環境の鍵を編集する。
gpg --homedir "$TEMP_GNUPGHOME" \
--edit-key "$PRIMARY_FINGERPRINT"
keytocardは、keyコマンドで選択した副鍵を対象にする。
副鍵の番号は鍵ごとに異なるため、編集画面のusageとフィンガープリントを次の対応表と照合する。
| usage | アルゴリズム | 保存先 |
|---|---|---|
| S | Ed25519 | Signature key |
| A | Ed25519 | Authentication key |
| E | Curve25519 | Encryption key |
一つの副鍵だけを選択し、keytocardで対応する保存先へ登録する。
選択中の副鍵には*が付く。
gpg> key <対象副鍵の番号>
gpg> keytocard
登録後は同じkeyコマンドで選択を解除し、残りの副鍵でも繰り返す。
gpg> key <対象副鍵の番号>
三つの副鍵を登録したら、編集内容を保存する。
gpg> save
saveすると、一時環境にあった各秘密副鍵の実体がカード参照用stubへ置き換わる。
通常環境の秘密副鍵には、この編集内容は保存されない。
カードと通常環境を確認する
まず、一時環境からYubiKeyの各スロットを確認する。
gpg --homedir "$TEMP_GNUPGHOME" --card-status
gpg --homedir "$TEMP_GNUPGHOME" \
--list-secret-keys \
--keyid-format LONG \
--with-subkey-fingerprint
Signature key、Authentication key、Encryption keyに、それぞれ対象の副鍵のフィンガープリントが表示されることを確認する。
一時環境の秘密鍵一覧では、カード上へ移した副鍵にssb>が表示される。
次に、--homedirを付けず、通常環境を確認する。
gpg --list-secret-keys \
--keyid-format LONG \
--with-subkey-fingerprint
通常環境では、S、A、Eの各副鍵が引き続きssbと表示され、ssb>へ変わっていないことを確認する。
YubiKeyを使って署名と復号を試す
--card-statusだけでは秘密鍵を使えることまで確認できないため、一時環境で署名を作成する。
printf 'YubiKey signing test\n' > "$TEMP_GNUPGHOME/test.txt"
gpg --homedir "$TEMP_GNUPGHOME" \
--armor \
--detach-sign "$TEMP_GNUPGHOME/test.txt"
gpg --homedir "$TEMP_GNUPGHOME" \
--verify "$TEMP_GNUPGHOME/test.txt.asc" \
"$TEMP_GNUPGHOME/test.txt"
次に、自分の公開鍵で暗号化したデータをYubiKeyで復号する。
gpg --homedir "$TEMP_GNUPGHOME" \
--armor \
--recipient "$PRIMARY_FINGERPRINT" \
--output "$TEMP_GNUPGHOME/test.txt.asc.gpg" \
--encrypt "$TEMP_GNUPGHOME/test.txt"
gpg --homedir "$TEMP_GNUPGHOME" \
--decrypt "$TEMP_GNUPGHOME/test.txt.asc.gpg"
認証副鍵は、SSHなど実際の認証先で確認する。
SSH向けのgpg-agent設定はこの記事の対象外とする。
一時環境を削除する
カード上の鍵を使えることと、通常環境に秘密副鍵が残っていることを確認してから、一時環境のgpg-agentを終了する。
gpgconf --homedir "$TEMP_GNUPGHOME" --kill all
削除対象がmktempで作成したパスであることを確認してから、一時環境を削除する。
case "$TEMP_GNUPGHOME" in
/tmp/tmp.*) rm -rf -- "$TEMP_GNUPGHOME" ;;
*) printf '想定外のパスです: %s\n' "$TEMP_GNUPGHOME" >&2 ;;
esac
unset TEMP_GNUPGHOME
unset PRIMARY_FINGERPRINT
この削除は、SSDなどに残ったデータを物理的に消去したことまでは保証しない。 削除後の復元まで脅威として考える場合は、暗号化した一時ファイルシステムや、ネットワークから隔離した使い捨て環境で作業する。
同じ秘密副鍵を二重保持する意味
YubiKeyへ登録した秘密鍵は、通常の秘密鍵ファイルとしてカードから再エクスポートできない。 そのため、YubiKeyは秘密鍵の利用場所にはなるが、バックアップにはならない。
一方、今回の構成ではPCにも同じ秘密副鍵が残る。 PCが侵害されて秘密副鍵を取得されれば、攻撃者はYubiKeyを持っていなくても、その副鍵で署名、認証、復号ができる。 YubiKeyへ登録しても、PC側の秘密副鍵はハードウェアで保護されない。
この構成の目的は、PC上の秘密副鍵を失わずにYubiKeyの運用を試すことにある。 動作とバックアップを確認した後、PC側をstubだけにするか、オフライン環境でYubiKey専用副鍵を作るかを次の段階で選ぶとよいだろう。