既存のSSH秘密鍵をWindows証明書ストアに置く


SSHの秘密鍵をWindowsに管理させれば、Windowsのログインパスワードと秘密鍵の復号化を紐付けられて便利なんじゃないかと思った。

注: 単にWindowsログインパスワードと鍵の暗号化を関連付けたいなら、KeePass + KeeAgent の方がマシなので通常の人間はそちらを使うべき。

概要

  • OpenSSHの id_rsa ファイルは生のRSA鍵ペアなので、これを openssl コマンドで PKCS#12 ファイルに変更することでWindowsにインポートできる。
  • インポートした鍵をSSH鍵として使うには、PuTTY-CACのようなCAPIに対応したクライアントを使う

SSH2の鍵は基本的にRSA署名が扱える鍵ならなんでも良く、必ずしもssh-keygenで作る必要性は無い。 gpg-agent に登録したPGP鍵を認証に使う( https://qiita.com/tsuyoshi_cho/items/79c09905ae3f192b3a0f )とか、yubikeyを認証に使う( https://qiita.com/dseg/items/77d77467970b1b510285 ) といった色々な方法が既に紹介されている。

今回は、GPGとかYubikeyの代わりに、Windows OS標準の公開鍵機構を使って、既存のOpenSSH鍵をインポートして使ってみる。

(この記事でやっている事は、 ユーザ証明書を使って認証しているわけではなく 、単にRSA鍵を証明書フォーマットに変換しているだけにしている。実際には、SSHプロトコル自体は真面目な証明書認証もサポートしている https://www.conoha.jp/guide/openssh.php この記事の手法は、サーバ側は単に .ssh/authorized_keys を使ったいわゆる普通のSSH公開鍵認証さえ設定されていれば良いのがポイントとなる。)

OpenSSH鍵を証明書に変換する

ssh-keygen で作った鍵ファイルのうち、 id_rsa は生のRSA鍵データとなっている。このままでは有効期限とか名前といったデータが欠けていて証明書として使うことはできないが、幾つかのパラメタ(名前CNや有効期限days)を補ってやることで証明書に変換することができる。

この手の変換作業は openssl コマンドで実施するのが定番になっている。適当な環境にopensslをインストールし、適当にコンフィグファイル(ここではwrap.conf)を用意し、

wrap.conf
distinguished_name=none
x509_extensions = usr_cert

[usr_cert]
keyUsage=digitalSignature

[none]

適当に自己署名証明書(out.cer)を生成し、

out.cerの生成
OPENSSL_CONF=wrap.conf openssl req -key ~/.ssh/id_rsa -days 7000 -x509 -subj /CN=my_ssh_key1 > out.cer

適当にPKCS#12形式でエクスポート(out.p12)すれば、

out.p12の生成
# エクスポート用のパスフレーズを要求されるが、空で良い
openssl pkcs12 -export -in out.cer -inkey ~/.ssh/id_rsa -name "My SSH Key1" > out.p12

Windowsにインポート可能なPKCS#12ファイルができる。

Windowsに秘密鍵をインポートする

生成されたファイル out.p12 をダブルクリックする("暗号化シェル拡張"で開く)と、証明書のインポートウィザードが始まる。(.cerには秘密鍵が入っていないため使えないのに注意)

ここでは 現在のユーザー を選択する。適当にウィザードを進めると鍵の強度設定になるが、ここは 秘密キーの保護を強力にする を選択し、このキーをエクスポート可能にするをOFFにする。後者は、元にした秘密鍵(id_rsaに入っていたもの)を再度取り出せるようにするかという選択で、アプリケーションの挙動には影響しないが特に理由の無い限りエクスポート不可の方が良い。

あとは "自動的に証明書ストアを選択する" 等を適当に選択し、保護レベルを設定してウィザードを完了させると、証明書が証明書ストアに登録される。

"秘密キーの保護を強力にする" を選択することで、知らないソフトが勝手に証明書を使うのを防ぐことができる。これが選択されている場合、未知のアプリから証明書の利用要求があると、Windowsはダイアログを表示してユーザに確認を求める。保護レベルをデフォルトの"中"から"高"にすると、更にパスフレーズを設定することもできる。

登録内容の確認

"ファイル名を指定して実行" から certmgr.msc を開き、 "個人" → "証明書" と選ぶとインポートした証明書が確認できる。

証明書をダブルクリックすると内容を確認できる:

ここではRoot CAに信頼されていないといった警告が出るが、 放置で良い 。SSHの公開鍵認証では、特に鍵がCAによって承認されている必要は無い。

このプロパティダイアログは、コマンドラインから証明書を登録する際に使用するfingerprintの取得にも使用する。

PuTTY-CACのPageantに鍵を置く

通常のPuTTYやMSのOpenSSH移植ではWindowsの鍵ストアにアクセスすることはできない。しかし、Yubikey界隈でよく使用されているPuTTY-CAC https://risacher.org/putty-cac/index-old.html を使うことで、Windowsの鍵ストアが認識している秘密鍵をSSH鍵として使用できる。

PuTTY-CACのリリースページ( https://github.com/NoMoreFood/putty-cac/releases )からpageant.exeをダウンロードして起動する。適切に鍵をインポートできていれば、"Add CAPI Cert"を選択した際にメニューで証明書を選択できる:

コマンドラインからのロード

流石にGUIで毎回ロードするのはキツいものがあるので、コマンドラインからロードする。通常のPuTTYの場合、Pageantを起動する際に、コマンドラインで証明書ファイル(.ppk)を指定することでロードできるが、PuTTY-CACではこれが拡張され証明書のfingerprintを指定できるようになっている。

fingerprintを確認するには、証明書のプロパティを使う:

(これはダミー証明書なのでキー使用法等が異なっている)

ここでは、拇印が 0309c343745244222037eb4b34415795836f4aa4 となっているので、コマンドラインには

pageant.exe CAPI:0309c343745244222037eb4b34415795836f4aa4

のように、 CAPI: を足して指定することでロードできる。

使う

後は適当にPuTTYのPageantを使えるクライアントで使用できる。

Cygwinで使用する場合は、Yubikeyのサイトにあるように https://developers.yubico.com/PGP/SSH_authentication/Windows.htmlssh-pageant を導入して

ssh-pageantの起動
eval $(/usr/bin/ssh-pageant -r -a "/tmp/.ssh-pageant-$USERNAME")

のようにすれば、Pageantにロードした秘密鍵情報をCygwinのSSHからも使用できる。

インポート時に"秘密キーの保護を強力にする"を選択した場合、初回の鍵使用時は

のようなダイアログが現われ、 OK をクリックするまで処理が保留される。

秘密鍵の保護手法として適切か?

一応、MS的には、 個人の証明書ストアに格納された秘密鍵はDPAPIで保護される ということになっている。

DPAPI(Data Protection API)で個人データを保護する場合は、 ユーザに紐付けられた512bitsのマスターキーをパスワードで暗号化 した上で使用しているので、Windowsのログオンパスワードで保護されているのと同様のレベルの保護があると言える。(ただし、この資料はちょっと古い)

ただ、UIとしてはあんまり強固にはなっていない。一応使用時点で承認を求めるように設定できるが、1回の起動で1度だけで、"N時間おきに確認を求める"といった設定ができない。せっかくWindows Helloとかが有ってEdgeのWebAuthn実装では適切に運用されているのに、証明書のような標準ベースのUIで使えないのはちょっと惜しい気がする。

Windows HelloをSSH秘密鍵の保護に使いたいなら、KeePass + KeeAgentのような外部のパスワードマネージャを使うしかないのが現状と言える。MSは OpenSSH server/clientのWin32移植 をメンテナンスして、これにはNTサービス化されたssh-agentまで含まれているが、今回のようにCAPIを使用した証明書の管理は一切サポートしていない。

もっとも、最近はMSも盛んにDevOpsフレンドリーを目指しているので、AppleとかUbuntuのようにOSのGUIレベルでssh-agentをサポートする時代がそのうち来るのかもしれない。