Перейти к содержимому
ecdsa.com
← Все статьи

ssh-keygen в 2026: типы ключей, парольные фразы и агенты

В версиях OpenSSH с 10.0 по 10.6 сменилось больше значений по умолчанию, чем заметило большинство: постквантовый обмен ключами, гибридный тип подписи ML-DSA, больше раундов KDF, сокеты агента в домашнем каталоге. Каждую команду ниже мы выполнили в OpenSSH 10.6p1 и привели её настоящий вывод.

Коротко. Выполните ssh-keygen -t ed25519 -C "you@host-year" и задайте для ключа парольную фразу; Ed25519 используется по умолчанию начиная с OpenSSH 9.5. Если у вас есть ключ безопасности FIDO2, выбирайте -t ed25519-sk (или -t ecdsa-sk, если аутентификатор не поддерживает Ed25519): тогда закрытый ключ вообще не хранится на диске. RSA используйте только для систем, которые не умеют ничего другого, и тогда длиной не меньше 3072 бит: с 14 октября 2026 года GitHub отклоняет новые ключи RSA меньшей длины. Загружайте ключи в агент с ограниченным сроком жизни (ssh-add -t 8h), не перенаправляйте агент на хосты, которые вы не контролируете (используйте ProxyJump), и привязывайте ключи к хостам назначения через ssh-add -h. Для парка серверов замените разрозненные записи в authorized_keys короткоживущими SSH-сертификатами. Постквантовая защита сеанса уже включена: OpenSSH 10 по умолчанию согласует mlkem768x25519-sha256 и предупреждает, если сервер этого не умеет. Новый гибридный тип подписи ssh-mldsa44-ed25519 (OpenSSH 10.6) стоит испытать, но делать его своим единственным ключом пока рано.

Команды выполнялись 20 октября 2026 года в 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 или новее
ecdsaECDSA на P-256, P-384 или P-521Политика требует кривых NIST
rsaRSA, по умолчанию 3072 бита начиная с OpenSSH 8.0Старые серверы, специализированные устройства, некоторые HSM
mldsa44-ed25519Гибридная подпись ML-DSA-44 + Ed25519, появилась в 10.6Испытание постквантовой аутентификации

DSA больше нет: его удалили в OpenSSH 10.0 (апрель 2025 года), «завершив процесс отказа от него, начатый в 2015 году». С ключами RSA всё в порядке: в OpenSSH 8.8 (сентябрь 2021 года) отключили алгоритм подписи ssh-rsa, который хеширует с помощью SHA-1. Тот же ключ RSA подписывает и алгоритмами rsa-sha2-256 или rsa-sha2-512, поэтому ошибку сервера про ssh-rsa обычно устраняют обновлением клиента, а не заменой ключа. GitHub рассуждает так же: 13 января 2027 года он уберёт поддержку подписей ssh-rsa после пробных отключений 4 ноября и 9 декабря 2026 года, а с 14 октября 2026 года принимает новые ключи RSA только длиной от 3072 бит.

Размеры ключей в том виде, в каком их записывает ssh-keygen 10.6 (строка в .pub включает комментарий):

Ключи RSA и ECDSA по умолчанию, гибридный ключ и ключ Ed25519 из следующего раздела
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
Вывод (OpenSSH 10.6p1)
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

Строка открытого гибридного ключа в 19 раз длиннее, чем у Ed25519. Это важно для файлов authorized_keys, сертификатов и всего, что хранит ключи в столбце базы данных, рассчитанном на Ed25519. Классическую пару подробно разбирает сравнение Ed25519 или RSA.

Генерация ключа

Генерация ключа Ed25519 с комментарием о том, где он хранится
ssh-keygen -t ed25519 -C "alice@laptop-2026" -N 'correct horse battery staple' -f ~/.ssh/id_ed25519
Вывод (OpenSSH 10.6p1)
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 и число раундов в заголовок, который любой может прочитать без парольной фразы:

kdfinfo.py: чтение шифра, KDF и числа раундов из закрытого ключа OpenSSH
"""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)
Раунды по умолчанию, затем время разблокировки при 32, 100 и 500 раундах
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
Вывод (одно ядро arm64 в Docker на Apple M4 Max)
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 отклоняет ключи, у которых больше миллиона раундов, поэтому вредоносный файл ключа уже не может подвесить сервис, который его разбирает.

Парольная фраза защищает файл при хранении. Она ничего не даёт, если ключ уже расшифрован в агенте на скомпрометированной машине; для этого случая и нужны аппаратные ключи.

Аппаратные ключи: -sk

Ключи ed25519-sk и ecdsa-sk (OpenSSH 8.2 и новее) хранят секрет в аутентификаторе FIDO2, например в YubiKey или в платформенном аутентификаторе. Файл ~/.ssh/id_ed25519_sk в этом случае содержит только дескриптор ключа, по которому аутентификатор восстанавливает ключ; без устройства файл бесполезен. Каждая подпись требует присутствия пользователя (касания), а с -O verify-required — ещё и PIN-кода. Важные параметры с цитатами из ssh-keygen(1):

  • -O resident сохраняет дескриптор ключа на аутентификаторе, поэтому ssh-keygen -K или ssh-add -K могут загрузить его на другом компьютере. Руководство предупреждает, что это «повышает вероятность того, что злоумышленник сможет воспользоваться украденным аутентификатором», поэтому сочетайте этот параметр с PIN-кодом.
  • -O verify-required требует проверки пользователя (сейчас это PIN-код) для каждой подписи; сервер может потребовать её опцией verify-required в authorized_keys.
  • -O application=ssh:work разделяет несколько резидентных ключей на одном устройстве; строка должна начинаться с ssh:.
  • -O no-touch-required отключает касание, а sshd отклоняет такие подписи, если authorized_keys явно их не разрешает.
  • -O write-attestation=path сохраняет аттестацию аутентификатора, с помощью которой организация может при регистрации ключа доказать, что он создан на одобренном оборудовании.
Типичная регистрация резидентного ключа под защитой PIN-кода (нужен аутентификатор FIDO2)
ssh-keygen -t ed25519-sk -O resident -O verify-required -O application=ssh:work -C "alice@yubikey-2026"

Эту команду мы не запускали: в контейнере нет аутентификатора, и без него в выводе была бы только ошибка. Некоторые старые аутентификаторы поддерживают только P-256; для них используйте ecdsa-sk. Платформенные ключи тоже работают, но у ключей, которые хранит защищённое оборудование ноутбука, свои компромиссы; они разобраны в статье об аппаратных ключах.

Постквантовая защита: обмен ключами сейчас, подписи потом

У двух частей SSH разные сроки. Сеансовые ключи получаются в результате обмена ключами; если его взломают позже, записанный трафик можно будет расшифровать (собери сейчас, расшифруй потом). Подписи хоста и пользователя важны только в момент подключения: будущий квантовый компьютер не сможет вернуться в прошлое и подделать вход, который уже состоялся. Поэтому в OpenSSH первым сменили обмен ключами:

  • sntrup761x25519-sha512 включён по умолчанию с OpenSSH 9.0 (2022);
  • mlkem768x25519-sha256 (ML-KEM-768 в сочетании с X25519) используется по умолчанию с OpenSSH 10.0 (апрель 2025 года) и теперь описан в RFC 10042 (информационный RFC, 2026);
  • с 10.1 клиент выводит предупреждение, если сеанс в итоге использует классический обмен ключами, а с 10.6 такой сеанс записывает в журнал и сервер.
ssh -v localhost true, настройки по умолчанию на обеих сторонах (отфильтровано через grep)
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

Мы заметили две детали. Клиент молчит, если вы сами ограничили KexAlgorithms классическим методом (предупреждение адресовано серверам), а сервер записывает в журнал каждый такой сеанс, например connection from ::1 port 38814 is not using a post-quantum key exchange algorithm: "curve25519-sha256" [preauth]. WarnWeakCrypto no-pq-kex в блоке Match host отключает предупреждение клиента для хоста, который вы не можете исправить, как советует openssh.com/pq.html. GitHub объявил о поддержке mlkem768x25519-sha256 на github.com с 14 октября 2026 года; 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 создала ssh_host_mldsa44_ed25519_key рядом с ключами хоста RSA, ECDSA и Ed25519, а сам тип стоит в конце списка HostKeyAlgorithms по умолчанию, поэтому клиенты по-прежнему выбирают Ed25519. Если запросить этот тип явно, вся цепочка работает:

ssh -v -o HostKeyAlgorithms=ssh-mldsa44-ed25519 -o IdentitiesOnly=yes -i ~/.ssh/id_mldsa44_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".

Обе половины подписывают одно и то же сообщение с разделением доменов, и «если не пройдёт любая из двух операций проверки, проверка составной подписи ДОЛЖНА завершиться неудачей», поэтому сегодня гибрид не слабее Ed25519. Платить за это приходится размером (по данным черновика, подпись в 2 484 байта при каждой аутентификации) и совместимостью: серверы, Git-хостинги, бастионы (промежуточные хосты) и библиотеки, которые не знают этого типа, ключ не примут. Сгенерируйте такой ключ, проверьте его на своих хостах и оставьте рядом с ним зарегистрированный ключ Ed25519.

Агент и перенаправление агента

ssh-agent хранит расшифрованные ключи и подписывает по запросу; программы обращаются к нему через сокет из SSH_AUTH_SOCK. С OpenSSH 10.1 этот сокет находится в ~/.ssh/agent, а не в /tmp. Добавляйте ключи с ограниченным сроком жизни, чтобы забытый сеанс не держал их разблокированными бесконечно:

Запуск агента и добавление ключа на 8 часов (стенд передаёт парольную фразу через SSH_ASKPASS)
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)

AddKeysToAgent в ~/.ssh/config делает то же самое при первом использовании ключа и принимает срок жизни (AddKeysToAgent 8h) или confirm: тогда агент, как и с ssh-add -c, запрашивает подтверждение перед каждой подписью.

Перенаправление агента (ssh -A, ForwardAgent yes) открывает доступ к этому сокету на удалённом хосте. ssh_config(5) прямо называет риск: «злоумышленник не может получить ключевой материал из агента, однако может выполнять операции с ключами». Любой, у кого есть root на удалённом хосте, может входить от вашего имени везде, где работают ваши ключи, пока вы не отключитесь. Лучше выработать две привычки:

  • Подключайтесь через промежуточный хост, а не перенаправляйте агент. ssh -J bastion target (или ProxyJump bastion в конфигурации) устанавливает второе соединение с вашей машины через перенаправленный TCP-поток; бастион никогда не получает доступа к вашему агенту.
  • Ограничивайте ключи. ssh-add -h (OpenSSH 8.9 и новее) привязывает ключ к хостам назначения, которые опознаются по их ключам хоста из known_hosts, а если вы всё же перенаправляете агент, то и к цепочкам переходов (bastion>target).
dest.sh (фрагмент): ключ, который работает только для prod-host
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 и не выдал для него ни одного ключа: ключ существует, но не для этого хоста назначения. Руководство честно говорит о пределах такой защиты: ограничение «не мешает злоумышленнику с доступом к удалённому SSH_AUTH_SOCK перенаправить его дальше и использовать на другом хосте (но только для разрешённого хоста назначения)».

Сертификаты вместо authorized_keys

Копировать открытые ключи в authorized_keys на каждый сервер — подход, который не масштабируется, а отзыв ключа при нём превращается в поиск по всем серверам. SSH-сертификат — это открытый ключ, подписанный ключом центра сертификации (CA) вместе с идентификатором, принципалами, серийным номером и сроком действия. Серверы один раз настраивают доверие к CA (TrustedUserCAKeys) и проверяют список отзыва (RevokedKeys):

Подпись ключа пользователя на 8 часов, вход, отзыв по серийному номеру, повторная попытка (от root на сервере стенда)
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'
Вывод (sshd запущен с TrustedUserCAKeys /etc/ssh/user_ca.pub и RevokedKeys /etc/ssh/revoked.krl)
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 при подписи, @cert-authority в known_hosts) точно так же убирают вопрос о доверии ключу хоста при первом подключении.

Ротация ключей

  • Ключи пользователей. Сгенерируйте новый ключ, добавьте его открытый ключ рядом со старым везде (серверы, Git-хостинги, секреты CI), подключитесь с -i new_key -o IdentitiesOnly=yes, чтобы убедиться, что он работает, и только потом удалите старую строку. Какая строка старая, подскажет комментарий с датой.
  • Ключи хоста. UpdateHostKeys включён по умолчанию с OpenSSH 8.5 (если вы не переопределили UserKnownHostsFile и не включили VerifyHostKeyDNS): после аутентификации сервер сообщает все свои ключи хоста, а клиент записывает новые, поэтому сервер может опубликовать замену до того, как выведет старый ключ из работы. Клиенты, которые подключаются редко, пропускают это окно; сертификаты хоста снимают проблему.
  • Алгоритмы. Тот же механизм позволяет провести миграцию: добавьте ключ хоста Ed25519 (а позже и гибридный), дайте клиентам его запомнить, затем удалите ключ RSA.
  • Инвентаризация. ssh-keygen -l -f authorized_keys выводит по одному отпечатку на строку; сравните их со строками Accepted publickey … SHA256: в журнале sshd, чтобы найти ключи, которыми никто не пользуется.

Типичные ошибки

  • Файл ключа доступен на чтение всем. ssh отказывается его использовать и выводит WARNING: UNPROTECTED PRIVATE KEY FILE!. Выполните chmod 600 для ключа и 700 для каталога .ssh.
  • Неверная парольная фраза или преобразованный ключ. incorrect passphrase supplied to decrypt private key и error in libcrypto выглядят похоже; вторая ошибка обычно означает, что файл повреждён или записан в формате, который эта сборка не умеет читать. Библиотекам парольную фразу нужно передавать явно, как показывает ошибка this private key is passphrase protected в Go.
  • Слишком много ключей в агенте. После MaxAuthTries (6) неудачных попыток sshd прекращает аутентификацию и разрывает соединение с ошибкой 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 только после того, как выясните, что именно произошло.

Чек-лист

  1. ssh-keygen -t ed25519 (или -t ed25519-sk с ключом безопасности), парольная фраза, комментарий с владельцем, машиной и годом.
  2. Увеличьте число раундов KDF до приемлемой для вас задержки (-a 100 здесь стоит около полусекунды).
  3. Один ключ на устройство; никогда не копируйте закрытые ключи между машинами.
  4. Агент со сроками жизни ключей; IdentitiesOnly yes для каждого хоста; ProxyJump вместо ForwardAgent; ssh-add -h, если без перенаправления не обойтись.
  5. Парк серверов: сертификаты пользователей и хостов с коротким сроком действия, ключ CA офлайн, KRL на каждом сервере.
  6. Ищите в выводе ssh -v строку kex: algorithm: mlkem768x25519-sha256; считайте постквантовое предупреждение заявкой на обновление сервера.
  7. Проверьте ключи mldsa44-ed25519 на своих серверах; держите классический ключ зарегистрированным, пока гибрид не начнут принимать все узлы.

Источники