SSH 公钥认证排查报告
一、问题背景
使用 ssh -i <私钥文件> 连接远程服务器时,出现两个疑问:
- 公钥已经放进服务器的
authorized_keys,为什么登录还要求Enter passphrase for key? - 输入 passphrase 之后,为什么仍然退回到要求输入服务器登录密码?
二、关键概念:两个”密码”是两回事
SSH 密钥认证里有两道独立的关卡,容易被混淆:
| 提示 | 含义 | 验证位置 | 与 authorized_keys 的关系 |
|---|---|---|---|
Enter passphrase for key |
解锁本地加密私钥的密码 | 本地机器 | 无关 |
xxx's password |
服务器账号登录密码 | 远程服务器 | 公钥认证失败后才回退到它 |
- passphrase:保护本地私钥文件的密码,只在本地验证,不会发送到服务器。只要私钥文件在生成时设置了加密,每次使用都会要求输入。
- 公钥是否在 authorized_keys:决定服务器认不认这把钥匙,和私钥本身是否加密没有任何关系。
认证流程:
- 本地:输入 passphrase 解密私钥;
- 客户端:用私钥对挑战做签名,把公钥发给服务器;
- 远程:服务器在
authorized_keys中查找该公钥并验证签名。
任意一步失败,都会回退到密码登录。
三、为什么会要求输入 passphrase(即使公钥已就位)
因为私钥文件在生成时被加密了。SSH 必须先用 passphrase 解密私钥,才能用它做签名。这是加密私钥的正常行为,不是配置错误,也与公钥是否在 authorized_keys 无关。
确认私钥是否加密
head -n 3 <私钥文件>
出现 Proc-Type: 4,ENCRYPTED 或解析时要求密码,即为加密私钥。
避免每次都输入 passphrase
# 推荐:用 ssh-agent 缓存,私钥仍保持加密
eval "$(ssh-agent -s)"
ssh-add <私钥文件> # 输入一次,本次会话内免输
# 不推荐:彻底去掉 passphrase(安全性下降)
ssh-keygen -p -f <私钥文件> # 旧密码验证后,新密码留空
四、服务器端权限检查(排除权限问题)
SSH 对权限要求严格,权限过松会导致公钥被拒。正确的权限应为:
drwx------ .ssh/ (700)
-rw------- authorized_keys (600)
修复命令:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R <用户>:<组> ~/.ssh
本次检查结果:目录与文件权限均正常(.ssh 为 700,authorized_keys 为 600,属主正确),权限不是本次故障原因。
五、私钥换行被破坏(本次问题原因之一)
私钥文件对格式非常敏感。在不同系统间复制、经复制粘贴或聊天/邮件工具传输时,常见以下破坏:
- 换行符从 Unix 的
LF变成 Windows 的CRLF; - 多行被合并成一行,或换行丢失;
- 开头/结尾的
-----BEGIN ... KEY-----/-----END ... KEY-----被拆行或粘连; - 文件末尾缺少换行符。
诊断
cat -A <私钥文件> | head -5 # 行尾出现 ^M 即为 CRLF
file <私钥文件> # 报告 "with CRLF line terminators" 即被污染
修复
dos2unix <私钥文件>
# 或
sed -i 's/\r$//' <私钥文件>
# 或
tr -d '\r' < <私钥文件> > <私钥文件>.fixed && mv <私钥文件>.fixed <私钥文件>
验证
ssh-keygen -y -f <私钥文件>
- 能正常输出公钥 → 格式已修复;
- 报错
invalid format/error in libcrypto→ 内容在传输中缺字符,需重新用scp/sftp完整拷贝一份原始私钥。
建议:私钥不要用复制粘贴传输,改用
scp/sftp,并用sha256sum两端比对校验。
六、用 verbose 模式定位(排查核心手段)
ssh -i <私钥文件> -vvv <用户>@<主机>
日志里要盯住的几行
| 日志片段 | 含义 |
|---|---|
Trying private key: <私钥文件> |
开始尝试这把私钥 |
sign_and_send_pubkey: ED25519 SHA256:... |
私钥成功加载并签名,公钥已发给服务器 |
we sent a publickey packet, wait for reply |
等待服务器判定 |
Server accepts key |
服务器接受(成功) |
Authentications that can continue: ... password |
公钥被拒,回退到密码 |
Authentication failed. |
认证失败 |
本次 verbose 结论
- 私钥被成功加载并完成签名(是一把 ED25519 密钥),换行问题已修复,私钥格式正常;
- 但服务器随后返回
Authentication failed,没有出现Server accepts key; - 结论:该私钥对应的公钥没有登记在目标账号的
authorized_keys里(本次为另一台主机、另一个账号,且私钥类型为 ED25519,与此前账号中的 RSA 密钥不同)。
七、最终解决方案:把正确的公钥装到目标服务器
# 本地:从私钥导出对应公钥
ssh-keygen -y -f <私钥文件> > <私钥文件>.pub
# 方式一:自动安装
ssh-copy-id -i <私钥文件>.pub <用户>@<主机>
# 方式二:手动追加(注意用 >> 不要覆盖),并修正权限
# 将 .pub 内容追加到服务器 ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
校验密钥是否匹配(通用方法)
# 本地私钥对应的公钥指纹
ssh-keygen -y -f <私钥文件> | ssh-keygen -lf -
# 服务器 authorized_keys 的指纹
ssh-keygen -lf ~/.ssh/authorized_keys
两边 SHA256:... 指纹一致 → 密钥匹配;不一致 → 本地私钥与服务器公钥不是一对。
八、排查结论小结
- 要求输入 passphrase 是加密私钥的正常现象,与公钥是否在
authorized_keys无关; - 第一台主机的失败,疑似私钥换行被破坏,
dos2unix/ 去 CRLF 后私钥恢复正常; - 第二台主机换行问题已解决,私钥能正常签名,但失败原因是对应公钥未登记在目标账号的
authorized_keys中; - 服务器端
.ssh与authorized_keys权限均正常,非权限问题; - 最终解决:确认密钥匹配后,把正确的公钥安装到目标账号的
authorized_keys。
九、通俗版复述(大白话)
把 SSH 登录想象成”用钥匙开门进别人家”:
- 你手里的私钥 = 一把钥匙。
- 服务器上的 authorized_keys = 这家门锁里登记过的”允许进门的钥匙清单”。
你遇到的两个困惑,其实是两件完全不同的事:
第一件:为什么开门前先要我输一个密码(passphrase)?
因为你这把钥匙本身被装在一个”带密码的保险盒”里。你给钥匙加密的时候设了个密码,所以每次想用这把钥匙,得先输密码把保险盒打开,拿出钥匙。这个密码只是你自己家保险盒的密码,跟门锁那边登不登记过你完全没关系。门锁那边登记没登记,是另一码事。
所以”要输 passphrase”是正常的,不代表配置错了。嫌每次输麻烦,可以用 ssh-add 让电脑先帮你记一次,之后这段时间就不用反复输了。
第二件:为什么输完 passphrase,门还是不开,反而让我输”家门密码”?
这说明:保险盒打开了、钥匙也掏出来了、你也拿去捅门锁了 —— 但门锁一看”这把钥匙不在我的允许清单里”,于是拒绝了你,只好退而求其次问你:”那你知道家门密码吗?”
这里面有两种常见原因:
钥匙在运输中被弄坏了(私钥换行问题):你把钥匙从一台电脑复制到另一台时,格式被搞乱了(典型的是 Windows 和 Linux 换行符不一样),导致钥匙”缺了个齿”,根本捅不进锁。修法就是用
dos2unix之类的命令把钥匙”掰正”,再用ssh-keygen -y -f确认钥匙能正常使用。门锁根本没登记过这把钥匙(公钥不在 authorized_keys):第二台服务器就是这种情况。钥匙本身是好的、也成功捅锁了,但这家门锁的”允许清单”里压根没有你这把钥匙的记录,自然不让你进。解决办法就是:把你这把钥匙对应的”公钥”抄一份,登记到那台服务器的 authorized_keys 清单里(用
ssh-copy-id一条命令就能搞定)。
最后一个小提醒:换电脑、换账号时,钥匙的”型号”也可能不一样(比如一台用的是 RSA,另一台用的是 ED25519)。要确认你现在用的这把钥匙,和目标服务器清单里登记的,确实是同一把。用 ssh-keygen -lf 比一下两边的”指纹”,一样就对,不一样就说明拿错钥匙或没登记。
一句话总结:输 passphrase 是开自己钥匙盒,正常;进不去是因为门锁没认你的钥匙——要么钥匙运输时坏了,要么钥匙没在门锁的清单里登记。