ssh-keygen —— 密钥对生成与 known_hosts 维护
一句话定义
ssh-keygen 是 OpenSSH 自带的工具,负责生成密钥对(公钥 + 私钥)、修剪 known_hosts 里的过期主机指纹、查看现有密钥的指纹和元信息。
典型场景
- 第一次配置开发机:
ssh-keygen -t ed25519生成你的私人密钥对 - 给 GitHub 加新账号 key 时:再生成一把
~/.ssh/id_ed25519_work - 云机器重装、IP 复用:
ssh-keygen -R <host>清掉旧指纹 - 检查私钥是哪种类型、什么时候生成的:
ssh-keygen -lf ~/.ssh/id_rsa
公私钥配对,原理要先懂
公私钥认证(asymmetric / public-key cryptography)的本质:
- 私钥 (
id_xxx):留在你本地,永远不外发 - 公钥 (
id_xxx.pub):可以随便发,往远端~/.ssh/authorized_keys里塞一份 - 登录时,ssh 客户端用私钥对一个 server 发来的挑战做签名 → 远端用公钥验证签名 → 验过就放行
所以记住:
- 把公钥复制到远端(
authorized_keys),不是私钥 - 同一把私钥可以认证给无数台机器(把公钥分发出去就行)
- 一个用户可以有多把私钥,给不同场景用(个人 GitHub / 公司 GitHub / 生产服务器 / 测试机)
authorized_keys 长这样:
ssh-ed25519 AAAAC3Nz...xJ user@laptop
ssh-rsa AAAAB3Nz...IDAQAB another-user@another-machine
一行一把公钥。客户端尝试的任何一把私钥,只要其公钥在这个文件里,就能登录。
基础用法
生成新密钥对
# 推荐:现代算法 ed25519
ssh-keygen -t ed25519 -C "yuanjun@hihuo 2026-05-27"
# 或:兼容性更广的 RSA 4096
ssh-keygen -t rsa -b 4096 -C "yuanjun@hihuo 2026-05-27"
# 给特定用途生成(不覆盖默认 key)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_work -C "work account"
跑完会问几个问题:
Enter file in which to save the key (~/.ssh/id_ed25519): ← 直接回车用默认路径
Enter passphrase (empty for no passphrase): ← 私钥密码(可选)
Enter same passphrase again:
生成两个文件:
~/.ssh/id_ed25519 ← 私钥,600 权限
~/.ssh/id_ed25519.pub ← 公钥,644 权限
-t 算法对比
| 算法 | 推荐度 | 备注 |
|---|---|---|
ed25519 | 首选 | 短(68 字符)、快、强(128-bit 等效安全)。OpenSSH 6.5+ 支持,2014 年至今的机器都有 |
rsa -b 4096 | 兼容选 | 老旧设备(路由器、嵌入式)只认 RSA。但比 ed25519 慢、长 |
rsa -b 2048 | 不要再用 | 强度勉强,被一些合规要求禁用 |
ecdsa | 避免 | 算法本身没问题,但有 NIST 后门质疑、生态用得少 |
dsa | 禁用 | OpenSSH 7+ 默认禁用 |
训练营建议:所有新机器用
ed25519。已有id_rsa的不用换(继续用就行),新需求统一用 ed25519。
-C comment 字段
ssh-keygen -t ed25519 -C "yuanjun@macbook 2026-05-27"
-C 写啥都不影响认证逻辑,但写清楚 谁生成的、什么时候生成的:
- 多年后清理
authorized_keys:能看出"哪些 key 是老员工的、可以删" - 多机器混用同一个账号:能看出"这条登录记录从哪台机器来"
工业实践:<user>@<machine> <YYYY-MM-DD> 或 <purpose>@<machine>。
-f 指定输出路径
默认会写 ~/.ssh/id_<algo>,但同一台机器要多个 key 时必须指定不同路径:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_work
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_personal
然后在 ~/.ssh/config 里给不同 Host 段指 IdentityFile 不同 key。
-N passphrase
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_deploy # 空密码(无 passphrase)
-N "" 给私钥设空密码 —— 适合自动化脚本用的 key(CI / deploy bot),让脚本不用每次输密码。
代价:私钥文件泄露 = 直接被人登录。人用的 key 一定要设 passphrase,配合 ssh-agent 解锁一次后免输入。
推送公钥到远端
方法 1:ssh-copy-id —— 首选
ssh-copy-id user@host # 默认推 ~/.ssh/id_*.pub
ssh-copy-id -i ~/.ssh/id_ed25519_work.pub m1 # 指定推某把
ssh-copy-id 帮你做了几件事:
- 用密码登录远端
- 创建
~/.ssh目录(如果不存在),权限 700 - 追加公钥到
~/.ssh/authorized_keys,权限 600 - 幂等:已存在不重复
方法 2:手动 cat + ssh —— 适合脚本批量推
PUBKEY=$(cat ~/.ssh/id_rsa.pub)
for ip in 10.0.24.28 10.0.24.29 10.0.24.30; do
sshpass -p "$IP_PWD" ssh -o StrictHostKeyChecking=accept-new root@$ip "
mkdir -p /root/.ssh && chmod 700 /root/.ssh
grep -qxF '$PUBKEY' /root/.ssh/authorized_keys 2>/dev/null \
|| echo '$PUBKEY' >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
"
done
注意 grep -qxF '...' || echo '...' >> —— 幂等关键:
-q:静默-x:整行匹配(避免子串误判)-F:把 pattern 当字面量(公钥里有+/=等正则特殊字符)
方法 3:远端没 ssh,本地直接编辑文件
很少见。比如远端是新装的 Ubuntu Cloud Image,要写 cloud-init 配置:
# cloud-config
ssh_authorized_keys:
- ssh-ed25519 AAAAC3...xJ user@laptop
known_hosts 维护
~/.ssh/known_hosts 存的是你信任过的远端 host key 指纹。每次连接,ssh 客户端对比远端指纹和本地记录,不一致就拒连(防止中间人攻击)。
看一条记录
ssh-keygen -F m1 # 查 m1 在不在 known_hosts
# m1 RSA SHA256:...
ssh-keygen -lf ~/.ssh/known_hosts | head
# 列出所有记录的指纹
清掉某个 host
云机器 IP 复用 / 机器重装 / 阿里云重新分配 IP,都会让远端 host key 变化,本地会报:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
修复:
ssh-keygen -R m1 # 按 alias 删
ssh-keygen -R 10.0.24.28 # 按 IP 删
ssh-keygen -R '[example.com]:2222' # 非默认端口的 host 要这样转义
-R 会就地修改 known_hosts、把旧记录注释成 # Host ... removed。
一次性临时跳过校验(仅限"反正是测试机")
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$ip
或写在 config 里(见 ssh-config.md)。
查看 / 比对密钥指纹
看私钥/公钥的指纹
ssh-keygen -lf ~/.ssh/id_ed25519 # 私钥指纹
ssh-keygen -lf ~/.ssh/id_ed25519.pub # 公钥指纹(应该一样)
# 256 SHA256:abcd... user@machine (ED25519)
跟 GitHub 上添加的指纹核对
GitHub Settings → SSH and GPG keys 里每把 key 都显示指纹。和本地 ssh-keygen -lf 输出对一下、确认你的本地 key 已经登记。
验远端 host key
第一次连陌生机器,对方应该公告它的 host key 指纹(云厂商 console、IDC 工单里)。比一下:
ssh -o StrictHostKeyChecking=ask m1
# The authenticity of host 'm1 (10.0.24.28)' can't be established.
# ED25519 key fingerprint is SHA256:xxxxx
# Are you sure you want to continue connecting (yes/no)?
对比无误后输 yes,本地记入 known_hosts,下次免问。
多 key 管理:IdentityFile + IdentitiesOnly yes
一台开发机有多把 key 时(个人 GitHub、公司 GitHub、生产服务器、测试机),靠 ~/.ssh/config 分配:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
Host prod-*
IdentityFile ~/.ssh/id_ed25519_prod
IdentitiesOnly yes
IdentitiesOnly yes 不是可选项,是必加项。
原因:默认情况下 ssh 会先尝试 ssh-agent 里所有 key,再用 IdentityFile 指定的。GitHub 看到第一把不匹配的 key 直接拒绝(甚至触发 rate limit)。IdentitiesOnly yes 让 ssh 严格只用 IdentityFile 指定的那把。
详见 ssh-config.md "场景 3:GitHub 多账号"。
常见踩坑
坑 1:生成时回车太快,把已有 key 覆盖了
ssh-keygen -t ed25519
Enter file in which to save the key (~/.ssh/id_ed25519): ← 默认路径
Overwrite (y/n)? ← 这里手快按 y 就完了
已有 key 千万别覆盖 —— 覆盖了,之前所有部署过这把公钥的机器全部认不出你。
防御做法:
- 生成新 key 时显式
-f ~/.ssh/id_ed25519_<purpose> - 在
~/.ssh/已经有id_*时,先ls ~/.ssh/看清楚
坑 2:私钥权限太宽,ssh 拒用
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/root/.ssh/id_rsa' are too open.
修复:
chmod 600 ~/.ssh/id_* # 所有私钥
chmod 644 ~/.ssh/id_*.pub # 公钥
chmod 700 ~/.ssh # 目录
chmod 600 ~/.ssh/config # config 也要 600
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/known_hosts
坑 3:把私钥 commit 进了 git
如果 ~/.ssh/id_rsa 不小心 commit 进 git 仓库(哪怕是 private 仓库),立刻视作泄漏:
- 生成新 key
- 在所有部署过的远端
~/.ssh/authorized_keys里删旧公钥 - GitHub 上从所有账号删旧 key
- 旧私钥从 git 历史里清掉(
git filter-repo或 BFG)—— 但即使清了,也应该认定泄漏过,不能再用
坑 4:authorized_keys 里有多行同样的公钥
手推 key 时 >> 反复追加 → 文件里同一个公钥出现 5 遍。不影响认证(认证逻辑是"任意一行匹配即通过"),但难维护。
修复(去重保留顺序):
awk '!seen[$0]++' ~/.ssh/authorized_keys > /tmp/auth && mv /tmp/auth ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
或者一开始就用幂等模式:grep -qxF '$PUBKEY' || echo '$PUBKEY' >>(见上面方法 2)。
坑 5:把公钥写错位置
echo "$PUBKEY" > ~/.ssh/authorized_keys # ❌ 单 > 覆盖原有内容
echo "$PUBKEY" >> ~/.ssh/authorized_keys # ✅ 双 >> 追加
血泪:> 写错 → 原 authorized_keys 里别人或自己其它 key 全没了 → 用户全部锁外面。
养成习惯:追加配置一律 >>,写新文件才 >。
坑 6:ed25519 老机器不认
no matching host key type found. Their offer: ssh-rsa
对方是老 ssh (OpenSSH < 6.5),不支持 ed25519。三种选择:
- 用 RSA key 连那台机器(推荐)
- 临时启用 RSA 算法:
ssh -o HostKeyAlgorithms=+ssh-rsa user@host - 升级远端 OpenSSH
关联命令
- ssh-config —— 把这些 key 分发到不同 Host 段
- ssh —— 用这把 key 实际登录
- sshd —— 远端必须允许 PubkeyAuthentication,否则你 key 再对也白搭
ssh-add—— 把私钥加进 ssh-agent,免每次输 passphrase