要点:ssh-keygen -t ed25519 -C "you@host-year" を実行し、鍵にパスフレーズを設定してください。Ed25519 は OpenSSH 9.5 以降のデフォルトです。FIDO2 セキュリティキーがあるなら -t ed25519-sk(認証器が Ed25519 に対応していない場合は -t ecdsa-sk)を優先してください。そうすれば秘密鍵がディスク上に置かれることはありません。RSA はほかの方式を扱えないシステムに限って使い、その場合も 3072 ビット以上にしてください。GitHub は 2026年10月14日以降、それより小さい RSA 鍵の新規登録を拒否しています。鍵は有効期間を指定してエージェントに読み込み(ssh-add -t 8h)、自分で管理していないホストにはエージェントを転送せず(ProxyJump を使います)、ssh-add -h で鍵を使える接続先を限定してください。多数のサーバーを運用しているなら、散在する authorized_keys のエントリを有効期間の短い SSH 証明書に置き換えてください。セッションのポスト量子保護はすでに有効です。OpenSSH 10 はデフォルトで mlkem768x25519-sha256 をネゴシエートし、サーバーが対応していない場合は警告を表示します。新しいハイブリッド署名タイプ ssh-mldsa44-ed25519(OpenSSH 10.6)は試す価値がありますが、唯一の鍵として導入するにはまだ早い段階です。
コマンドは 2026年10月20日に、debian:forky-20261005-slim 上で Debian パッケージの openssh-client と openssh-server 1:10.6p1-1 を使って実行しました。鍵はすべてこの記事のために生成したものです。
どの鍵タイプを使うか
-t | 内容 | 使う場面 |
|---|---|---|
ed25519(デフォルト) | Ed25519、32 バイトの公開鍵、決定的な署名 | ほぼすべての場合 |
ed25519-sk、ecdsa-sk | FIDO2 鍵:秘密情報は認証器の中にとどまり、署名のたびにタッチが必要 | セキュリティキーがあり、サーバーが OpenSSH 8.2 以降を実行している場合 |
ecdsa | P-256、P-384、P-521 上の ECDSA | ポリシーで NIST 曲線が求められる場合 |
rsa | RSA、OpenSSH 8.0 以降のデフォルトは 3072 ビット | 古いサーバー、アプライアンス、一部の HSM |
mldsa44-ed25519 | ML-DSA-44 + Ed25519 のハイブリッド署名、10.6 で新たに追加 | ポスト量子認証のテスト |
DSA はもうありません。OpenSSH 10.0(2025年4月)で削除され、これによって「2015年に始まった非推奨化のプロセスが完了」しました。RSA 鍵自体は問題ありません。OpenSSH 8.8(2021年9月)で無効になったのは、SHA-1 でハッシュする ssh-rsa 署名アルゴリズムです。同じ RSA 鍵でも rsa-sha2-256 や rsa-sha2-512 で署名できます。そのため、ssh-rsa に関するサーバーのエラーは、たいてい鍵を作り直すのではなく、クライアントをアップグレードすれば解決します。GitHub も同じ考え方です。2026年11月4日と 12月9日の一時的な無効化(ブラウンアウト)を経て、2027年1月13日に ssh-rsa 署名を廃止します。また、2026年10月14日以降、新しい RSA 鍵は 3072 ビット以上のものしか受け付けていません。
ssh-keygen 10.6 が書き出したサイズは次のとおりです(.pub の行にはコメントも含まれます)。
ssh-keygen -q -t rsa -N '' -f ~/rsa_default
ssh-keygen -q -t ecdsa -N '' -f ~/ecdsa_default
ssh-keygen -q -t mldsa44-ed25519 -N '' -f ~/.ssh/id_mldsa44_ed25519
for f in ~/rsa_default ~/ecdsa_default ~/.ssh/id_ed25519 ~/.ssh/id_mldsa44_ed25519; do
ssh-keygen -l -f "$f.pub"
printf ' public key line: %s bytes, private key file: %s bytes\n' "$(wc -c < "$f.pub")" "$(wc -c < "$f")"
done
3072 SHA256:NGv36ZHCwauN4GGrdKSDZPnKTn6nFuLSVRS01/eSXOs alice@59151e7fff6c (RSA)
public key line: 572 bytes, private key file: 2602 bytes
256 SHA256:4ODqYFVTGSwjx7pfis5ZtZ02PKaSd15alII7RIHPhE8 alice@59151e7fff6c (ECDSA)
public key line: 180 bytes, private key file: 513 bytes
256 SHA256:I72RDcR38Z+glA2IhaHUj/h844afNwkK9siDEGHv24c alice@laptop-2026 (ED25519)
public key line: 99 bytes, private key file: 464 bytes
512 SHA256:oqHqur1575V136Khl2P7Gaa6rM+2dPT51B6YfjLSxB4 alice@59151e7fff6c (MLDSA44-ED25519)
public key line: 1868 bytes, private key file: 3982 bytes
ハイブリッド鍵の公開鍵の行は、Ed25519 の 19 倍の大きさです。これは authorized_keys ファイルや証明書、そして Ed25519 に合わせたサイズのデータベース列に鍵を保存するあらゆる仕組みに影響します。従来型の組み合わせについては、Ed25519 と RSA の比較で詳しく解説しています。
鍵の生成
ssh-keygen -t ed25519 -C "alice@laptop-2026" -N 'correct horse battery staple' -f ~/.ssh/id_ed25519
Created directory '/home/alice/.ssh'.
Generating public/private ed25519 key pair.
Your identification has been saved in /home/alice/.ssh/id_ed25519
Your public key has been saved in /home/alice/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:I72RDcR38Z+glA2IhaHUj/h844afNwkK9siDEGHv24c alice@laptop-2026
The key's randomart image is:
+--[ED25519 256]--+
| ..o*..... |
| o . .=... =. |
|. o .. +. + o. |
| . . . o =. . ...|
| o + S .. ..|
| . . o + B |
| . * = * o . |
| o E = o.+ |
| o oo. . |
+----[SHA256]-----+
ここで -N を使ってパスフレーズをコマンドラインで渡しているのは、検証環境をスクリプトで動かしているからにすぎません。対話的に使う場合はこれを省略すれば ssh-keygen が入力を求めるので、パスフレーズがシェルの履歴に残りません。コメントは公開鍵に保存される自由形式のテキストです。名前、マシン、年を入れておけば、何年も後になっても authorized_keys を監査できます。フィンガープリント(SHA256:…)はサーバーがログに記録する値なので、どこかに鍵を登録するときは控えておいてください。
パスフレーズと KDF のラウンド数
パスフレーズがなければ、秘密鍵ファイルは鍵そのものです。盗まれたバックアップ、ノートパソコンのディスクイメージ、設定を誤った同期フォルダーから、そのまま鍵が渡ってしまいます。パスフレーズがあれば、ファイルは bcrypt_pbkdf で導出した鍵で暗号化されます。OpenSSH 秘密鍵形式は、暗号方式、KDF、そのラウンド数をヘッダーに記録しており、このヘッダーはパスフレーズがなくても誰でも読めます。
"""Print the cipher, KDF and bcrypt rounds from the header of an OpenSSH private key file (openssh-key-v1)."""
import base64
import struct
import sys
text = open(sys.argv[1]).read()
body = "".join(line for line in text.splitlines() if not line.startswith("-----"))
blob = base64.b64decode(body)
magic = b"openssh-key-v1\0"
assert blob.startswith(magic), "not an OpenSSH private key"
pos = len(magic)
def string():
global pos
(length,) = struct.unpack(">I", blob[pos : pos + 4])
value = blob[pos + 4 : pos + 4 + length]
pos += 4 + length
return value
cipher = string().decode()
kdf = string().decode()
options = string()
line = f"cipher={cipher} kdf={kdf}"
if kdf == "bcrypt":
(salt_len,) = struct.unpack(">I", options[:4])
(rounds,) = struct.unpack(">I", options[4 + salt_len : 8 + salt_len])
line += f" salt={salt_len} bytes rounds={rounds}"
print(line)
python3 kdfinfo.py ~/.ssh/id_ed25519
for r in 32 100 500; do
ssh-keygen -q -p -a $r -P 'correct horse battery staple' -N 'correct horse battery staple' -f ~/.ssh/id_ed25519 >/dev/null
s=$(date +%s.%N); ssh-keygen -y -P 'correct horse battery staple' -f ~/.ssh/id_ed25519 >/dev/null; e=$(date +%s.%N)
printf 'rounds=%s unlock=%.2fs\n' $r $(echo "$e - $s" | bc)
done
cipher=aes256-ctr kdf=bcrypt salt=16 bytes rounds=32
rounds=32 unlock=0.16s
rounds=100 unlock=0.52s
rounds=500 unlock=2.59s
デフォルトは OpenSSH 9.4 より前が 16 ラウンド、9.4 からは 24 ラウンドで、10.6 以降は 32 ラウンドです。10.6 のリリースノートが指摘しているとおり(「指数関数的な bcrypt(3) とは異なる」)、コストはラウンド数に比例するので、上の数値は予測どおりに増えます。ロック解除の待ち時間として許容できる最大の値を選んでください。ファイルをコピーした攻撃者も、推測のたびに同じコストを払うからです。ssh-keygen -p -a 100 -f ~/.ssh/id_ed25519 で既存の鍵を再暗号化できます。セッションごとに一度エージェントに読み込めば、待ち時間も一度で済みます。10.6 以降の OpenSSH は 1,000,000 ラウンドを超える鍵を拒否するので、悪意のある鍵ファイルで、それを解析するサービスを止めることはできません。
パスフレーズが守るのは、保存されている状態のファイルです。侵害されたマシン上のエージェントで鍵が復号された後は、何の役にも立ちません。そこで役立つのが、ハードウェアで保護された鍵です。
ハードウェアで保護された鍵:-sk
ed25519-sk 鍵と ecdsa-sk 鍵(OpenSSH 8.2 以降)は、YubiKey などの FIDO2 認証器やプラットフォーム認証器の中に秘密情報を保持します。このとき ~/.ssh/id_ed25519_sk ファイルに入っているのは、認証器が鍵を再構成するのに必要な鍵ハンドルだけで、デバイスがなければ役に立ちません。署名のたびにユーザー存在確認(タッチ)が必要で、-O verify-required を付けると PIN も必要になります。重要なオプションを ssh-keygen(1) から引用します。
-O residentは鍵ハンドルを認証器に保存するので、別のコンピューターでもssh-keygen -Kやssh-add -Kで読み込めます。マニュアルは、これが「攻撃者が盗んだ認証器デバイスを使える可能性を高める」と警告しているので、PIN と組み合わせてください。-O verify-requiredは、署名ごとにユーザー検証(現時点では PIN)を要求します。サーバー側でもauthorized_keysのverify-requiredオプションでこれを必須にできます。-O application=ssh:workは、同じデバイス上の複数のレジデントキーを区別します。文字列はssh:で始まらなければなりません。-O no-touch-requiredはタッチを不要にします。ただし sshd は、authorized_keysで明示的に許可されていない限り、そうした署名を拒否します。-O write-attestation=pathは認証器のアテステーションを保存します。これにより組織は、登録時に、その鍵が承認済みのハードウェアで作成されたことを証明できます。
ssh-keygen -t ed25519-sk -O resident -O verify-required -O application=ssh:work -C "alice@yubikey-2026"
このコマンドは実行していません。コンテナーには認証器がなく、認証器なしで実行した記録ではエラーが表示されるだけだからです。古い認証器の中には P-256 しか実装していないものがあり、その場合は ecdsa-sk を使ってください。プラットフォームの鍵も使えますが、ノートパソコンのセキュアなハードウェアが保持する鍵には固有のトレードオフがあります。詳しくはハードウェアで保護された鍵を参照してください。
ポスト量子:鍵交換は今、署名はこれから
SSH を構成する 2 つの部分では、対応の期限が異なります。セッション鍵は鍵交換から得られます。鍵交換が後から破られると、記録されていた通信が解読されてしまいます(harvest now, decrypt later、つまり「今収集し、後で解読」)。ホストとユーザーの署名が意味を持つのは接続の瞬間だけです。将来の量子コンピューターがさかのぼって、すでに行われたログインを偽造することはできません。そのため OpenSSH は、鍵交換を先に切り替えました。
sntrup761x25519-sha512は、OpenSSH 9.0(2022年)以降デフォルトで有効です。mlkem768x25519-sha256(ML-KEM-768 と X25519 の組み合わせ)は OpenSSH 10.0(2025年4月)以降のデフォルトで、現在は RFC 10042(Informational、2026年)で規定されています。- 10.1 以降は、セッションが従来型の鍵交換にフォールバックするとクライアントが警告を表示し、10.6 以降はサーバーがそれをログに記録します。
debug1: kex: algorithm: mlkem768x25519-sha256
debug1: kex: host key algorithm: ssh-ed25519
debug1: Server host key: ssh-ed25519 SHA256:Qw4tNHnCo8UwS2qNXROrEm8RP+d+ccaVbwAzv8lLX+I
debug1: Offering public key: /home/alice/.ssh/id_ed25519 ED25519 SHA256:I72RDcR38Z+glA2IhaHUj/h844afNwkK9siDEGHv24c agent
debug1: Server accepts key: /home/alice/.ssh/id_ed25519 ED25519 SHA256:I72RDcR38Z+glA2IhaHUj/h844afNwkK9siDEGHv24c agent
Authenticated to localhost ([::1]:22) using "publickey".
KexAlgorithms curve25519-sha256,ecdh-sha2-nistp256 に制限したサーバーに対して、デフォルト設定のクライアントは次のように出力しました。
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
実際に確認できた詳細が 2 つあります。自分で KexAlgorithms を従来型の方式に制限した場合、クライアントは何も表示しません(警告の対象はサーバーです)。また、サーバーはそうしたセッションをすべてログに記録します。たとえば connection from ::1 port 38814 is not using a post-quantum key exchange algorithm: "curve25519-sha256" [preauth] のような行です。修正できないホストについては、openssh.com/pq.html が提案しているとおり、Match host ブロックの中に WarnWeakCrypto no-pq-kex を書けばクライアントの警告を止められます。GitHub は 2026年10月14日から github.com で mlkem768x25519-sha256 を提供すると発表しており、10月20日には今回の 10.6 クライアントが git@github.com との間でこの方式をネゴシエートしました。
署名はその次の段階です。OpenSSH 10.4 で ML-DSA-44 と Ed25519 を組み合わせた実験的な複合署名が追加され、10.6 で ssh-mldsa44-ed25519 という名前で有効になりました。この方式は SSHM ワーキンググループのドラフト draft-ietf-sshm-composite-sigs で規定されています。10.4 の実験的なコードで作成した鍵は作り直す必要があります。Debian の 10.6 パッケージでは、ssh-keygen -A が RSA、ECDSA、Ed25519 のホスト鍵と並べて ssh_host_mldsa44_ed25519_key を作成しました。このタイプはデフォルトの HostKeyAlgorithms の末尾にあるため、クライアントは引き続き Ed25519 を選びます。明示的に指定すれば、最初から最後まで問題なく動作します。
debug1: kex: host key algorithm: ssh-mldsa44-ed25519
debug1: Server host key: ssh-mldsa44-ed25519 SHA256:UVOUu+csMH8i8Zd/oWzokAGfzRYFetOncVzB4vYeMgQ
debug1: Server accepts key: /home/alice/.ssh/id_mldsa44_ed25519 MLDSA44-ED25519 SHA256:oqHqur1575V136Khl2P7Gaa6rM+2dPT51B6YfjLSxB4 explicit
Authenticated to localhost ([::1]:22) using "publickey".
両方の要素がドメイン分離された同じメッセージに署名し、「どちらか一方の検証処理が失敗した場合は、複合署名の検証を失敗させなければならない(MUST)」とされているため、このハイブリッドは現時点で Ed25519 より弱いことはありません。代償はサイズ(ドラフトによれば認証ごとに 2,484 バイトの署名)と互換性です。このタイプを知らないサーバー、Git ホスティングサービス、踏み台サーバー、ライブラリは、この鍵を受け付けません。ハイブリッド鍵を生成して自分が運用するホストで試し、Ed25519 鍵も並行して登録したままにしておいてください。
エージェントとエージェント転送
ssh-agent は復号済みの鍵を保持し、要求に応じて署名します。プログラムは SSH_AUTH_SOCK が示すソケットを通じてエージェントにアクセスします。OpenSSH 10.1 以降、このソケットは /tmp ではなく ~/.ssh/agent の下に置かれます。閉じ忘れたセッションが鍵をいつまでもロック解除したままにしないよう、有効期間を指定して鍵を追加してください。
eval "$(ssh-agent -s)" >/dev/null
echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
ssh-add -t 8h ~/.ssh/id_ed25519
ssh-add -l
SSH_AUTH_SOCK=/home/alice/.ssh/agent/s.eaeHp0xOjD.local.660.o5M944i4eW
Identity added: /home/alice/.ssh/id_ed25519 (alice@laptop-2026)
Lifetime set to 08:00:00
256 SHA256:I72RDcR38Z+glA2IhaHUj/h844afNwkK9siDEGHv24c alice@laptop-2026 (ED25519)
~/.ssh/config の AddKeysToAgent は、初回使用時に同じことを行います。有効期間(AddKeysToAgent 8h)か、ssh-add -c と同様に署名のたびに確認を求める confirm を指定できます。
エージェント転送(ssh -A、ForwardAgent yes)は、このソケットをリモートホスト上に公開します。ssh_config(5) は、そのリスクをはっきり述べています。「攻撃者がエージェントから鍵データを取得することはできないが、鍵を使った操作は実行できる」。リモートホストの root 権限を持つ者なら誰でも、接続が続いている間ずっと、その鍵が通用するあらゆる場所に本人としてログインできます。より良い習慣が 2 つあります。
- 転送せずにジャンプする:
ssh -J bastion target(または設定ファイルのProxyJump bastion)は、目的のホストへの接続を、転送された TCP ストリームを通して自分のマシンから行います。踏み台サーバーがエージェントに触れることはありません。 - 鍵を制限する:
ssh-add -h(OpenSSH 8.9 以降)は、known_hostsのホスト鍵で識別される接続先に鍵を結び付けます。転送する場合は、経由するホップ(bastion>target)にも結び付けます。
ssh-add -h prod-host ~/agent_only_key
ssh -o BatchMode=yes prod-host 'echo signed in on prod-host'
ssh -v -o BatchMode=yes build-host true 2>&1 | grep -iE 'agent|Offering|Permission denied|Authenticated|identity'
Identity added: /home/alice/agent_only_key (alice@laptop)
signed in on prod-host
debug1: get_agent_identities: bound agent to hostkey
debug1: get_agent_identities: ssh_fetch_identitylist: agent contains no identities
debug1: Offering public key: /home/alice/.ssh/id_ed25519 ED25519 SHA256:MA2nEhBnMlCkX3pWWgPtff90dRZF9D9k8i2svCUK1II
alice@127.0.0.2: Permission denied (publickey,password).
エージェントはセッションを build-host のホスト鍵に結び付け、そのうえでこのホストに対しては identity(秘密鍵)を一つも返しませんでした。鍵は存在しますが、その接続先向けではないということです。マニュアルはこの限界について慎重に書いています。制約は「リモートの SSH_AUTH_SOCK にアクセスできる攻撃者が、それを再び転送して別のホストで使うことは防げない(ただし、許可された接続先に対してのみ)」というものです。
authorized_keys に代わる証明書
すべてのサーバーの authorized_keys に公開鍵をコピーするやり方は規模が大きくなると立ち行かず、失効させるたびに鍵を探し回ることになります。SSH 証明書とは、ID、プリンシパル、シリアル番号、有効期間を付けて CA 鍵で署名した公開鍵です。サーバーは CA を一度だけ信頼し(TrustedUserCAKeys)、失効リストを確認します(RevokedKeys)。
ssh-keygen -s /etc/ssh/user_ca -I "alice@example.com 2026-10-20" -n alice -V +8h -z 1042 /home/alice/.ssh/id_cert_user.pub
ssh-keygen -L -f /home/alice/.ssh/id_cert_user-cert.pub
su - alice -c 'ssh -o IdentitiesOnly=yes -o IdentityAgent=none -i ~/.ssh/id_cert_user localhost "echo logged in with certificate"'
echo "serial: 1042" > /tmp/revoke.txt
ssh-keygen -k -u -s /etc/ssh/user_ca.pub -f /etc/ssh/revoked.krl /tmp/revoke.txt
ssh-keygen -Q -f /etc/ssh/revoked.krl /home/alice/.ssh/id_cert_user-cert.pub
su - alice -c 'ssh -o IdentitiesOnly=yes -o IdentityAgent=none -o BatchMode=yes -i ~/.ssh/id_cert_user localhost true'
Signed user key /home/alice/.ssh/id_cert_user-cert.pub: id "alice@example.com 2026-10-20" serial 1042 for alice valid from 2026-10-20T14:33:00 to 2026-10-20T22:34:33
/home/alice/.ssh/id_cert_user-cert.pub:
Type: ssh-ed25519-cert-v01@openssh.com user certificate
Public key: ED25519-CERT SHA256:eVstR5lK6VBztjl2AYyWcv8C6xwNahEEVSrQpe7Y/LE
Signing CA: ED25519 SHA256:+URM6AMiGUu+gv/gxoQgpOhHP4CUN1fznXWDlVxorbY (using ssh-ed25519)
Key ID: "alice@example.com 2026-10-20"
Serial: 1042
Valid: from 2026-10-20T14:33:00 to 2026-10-20T22:34:33
Principals:
alice
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
logged in with certificate
Revoking from /tmp/revoke.txt
/home/alice/.ssh/id_cert_user-cert.pub (alice@ci-runner): REVOKED
alice@localhost: Permission denied (publickey).
sshd は key ED25519-CERT SHA256:eVst… revoked by file /etc/ssh/revoked.krl とログに記録しました。デフォルトの拡張に注意してください。-O clear を付けて署名し、必要なものだけを追加し直さない限り(-O permit-pty)、証明書はエージェント転送とポートフォワーディングを許可します。CA 鍵はオフラインか HSM に保管し、証明書は年単位ではなく時間単位で発行してください。有効期間が短ければ、ほとんどの失効は不要になります。ホスト証明書(署名時の -h、known_hosts の @cert-authority)を使えば、同じ仕組みで、初回接続時に鍵を信頼するかどうかを尋ねる確認をなくせます。
鍵のローテーション
- ユーザー鍵:新しい鍵を生成し、その公開鍵をあらゆる場所(サーバー、Git ホスティングサービス、CI のシークレット)で古い鍵の隣に追加します。
-i new_key -o IdentitiesOnly=yesで接続して動作を確認してから、古い行を削除します。コメントに日付があれば、どの行が古いかがわかります。 - ホスト鍵:
UpdateHostKeysは OpenSSH 8.5 以降デフォルトで有効です(UserKnownHostsFileを上書きしたりVerifyHostKeyDNSを有効にしたりしていない場合)。認証後にサーバーが自分のホスト鍵をすべて通知し、クライアントが新しい鍵を記録するので、サーバーは古い鍵を廃止する前に後継の鍵を公開できます。まれにしか接続しないクライアントはこの期間を逃してしまいますが、ホスト証明書を使えばこの問題を避けられます。 - アルゴリズム:同じ仕組みで移行もできます。Ed25519 の(さらに将来はハイブリッドの)ホスト鍵を追加し、クライアントに学習させてから、RSA 鍵を削除します。
- 棚卸し:
ssh-keygen -l -f authorized_keysは行ごとにフィンガープリントを出力します。これを sshd のAccepted publickey … SHA256:というログ行と照合すれば、誰も使っていない鍵が見つかります。
よくある間違い
- 誰でも読める鍵ファイル:
sshはその鍵を使わず、WARNING: UNPROTECTED PRIVATE KEY FILE! と表示します。鍵にはchmod 600を、.sshディレクトリには700を設定してください。 - パスフレーズの誤り、または変換された鍵:incorrect passphrase supplied to decrypt private key と error in libcrypto は似ていますが、後者はたいてい、ファイルが壊れているか、そのビルドでは読めない形式であることを意味します。Go の this private key is passphrase protected が示すように、ライブラリにはパスフレーズを明示的に渡す必要があります。
- エージェント内の鍵が多すぎる:sshd は認証に
MaxAuthTries(6)回失敗すると諦め、Too many authentication failuresを返して切断します。クライアントが試す鍵は、それぞれがこの試行回数を消費することがあります(OpenSSH 10.6 のサーバーは、「この鍵を受け付けるか」という問い合わせを追加で 6 回まで許可します。PubkeyOptions max-pk-ok)。ホストごとにIdentityFileとIdentitiesOnly yesを設定してください。提示したどの鍵も登録されていなければ、Permission denied (publickey) になります。 - エージェントが拒否する:agent refused operation は、サーバーは鍵を受け付けたもののエージェントが署名しなかったことを意味します。FIDO 鍵に時間内にタッチしなかった、エージェントが確認画面を表示できない、トークンがロックされている、ディスク上の鍵ファイルがエージェントの保持する鍵ともう一致しない、といった原因が考えられます。
- 古い接続相手:no matching host key type found. Their offer: ssh-rsa は、サーバーが SHA-1 でしか署名できないことを意味します。サーバーをアップグレードしてください。ホストごとの
HostKeyAlgorithms +ssh-rsaはあくまで一時的な例外であり、決してグローバルな設定にしてはいけません。 - 変更されたホスト鍵を無視する:REMOTE HOST IDENTIFICATION HAS CHANGED は、別の経路で確認できる計画的なローテーションか、このチェックが本来検出すべき攻撃そのもののどちらかです。どちらなのかを確かめてから、
ssh-keygen -R hostで古い行を削除してください。
チェックリスト
ssh-keygen -t ed25519(セキュリティキーがあれば-t ed25519-sk)、パスフレーズ、所有者・マシン・年を含むコメント- KDF のラウンド数を許容できる待ち時間まで引き上げる(この環境では
-a 100で約 0.5 秒) - デバイスごとに個別の鍵を用意し、秘密鍵をマシン間で決してコピーしない
- 有効期間付きのエージェント、ホストごとの
IdentitiesOnly yes、ForwardAgentではなくProxyJump、転送が避けられない場合はssh-add -h - 多数のサーバー:有効期間の短いユーザー証明書とホスト証明書、オフラインの CA 鍵、すべてのサーバーに KRL
ssh -vでkex: algorithm: mlkem768x25519-sha256を確認し、ポスト量子の警告はサーバーのアップグレード案件として扱うmldsa44-ed25519鍵を自分のサーバーでテストし、すべての接続相手がハイブリッドを受け付けるまで従来型の鍵を登録したままにする
出典
- OpenSSH リリースノート:8.5(UpdateHostKeys のデフォルト化)、8.8(RSA/SHA-1 の無効化)、8.9(接続先の制約)、9.0(sntrup761x25519)、9.4 と 10.6(KDF ラウンド数)、9.5(Ed25519 のデフォルト化)、10.0(ML-KEM のデフォルト化、DSA の削除)、10.1(警告、エージェントのソケット)、10.4 と 10.6(ML-DSA-44 + Ed25519)
- OpenSSH:ポスト量子暗号
- マニュアルページ:ssh-keygen(1)、ssh-add(1)、ssh_config(5)
- RFC 10042:SSH における ML-KEM を用いた PQ/T ハイブリッド鍵交換
- draft-ietf-sshm-composite-sigs:SSH におけるポスト量子複合署名
- GitHub の変更履歴:SSH のセキュリティ改善(2026年9月22日)