记一次 TortoiseGit SSH 认证失败的排查与修复
用 TortoiseGit 拉取自托管 Gitea 仓库时,弹出了 TortoiseGitPlink 的安全提示,确认后却提示 "No supported authentication methods available"。折腾了一圈才把问题全部解决,记录一下排查思路和解决方案。
环境
- 远端:自托管 Gitea,端口 2222,SSH 协议
- 客户端:Windows 11,TortoiseGit,OpenSSH
- 密钥:Ed25519,最初为 PuTTY
.ppk格式
排查路线
1. 主机密钥缓存
首次连接 SSH 服务器时,TortoiseGitPlink 会弹出安全提示框,询问是否信任服务器的主机密钥。点击"是"即可缓存,这是正常的 SSH 安全机制。
2. Pageant 进程干扰
确认主机密钥后仍然失败,报 "Server refused our key"。检查任务管理器发现 Pageant(PuTTY 认证代理)正在后台运行。
TortoiseGitPlink 默认会优先使用 Pageant 中已加载的密钥,而不是远端设置中指定的 .ppk 文件。如果 Pageant 中没有加载正确的密钥,就会认证失败。
在 Pageant 中添加 .ppk 文件后仍然失败,决定放弃 TortoiseGitPlink,改用系统自带的 OpenSSH。
3. 密钥格式转换
TortoiseGit 的 SSH 客户端切换到 OpenSSH 后,需要 OpenSSH PEM 格式的私钥,而不是 .ppk。
PuTTYgen 的 GUI 可以手动做转换,但命令行参数在这个版本中不理想。最后用 Python 的 cryptography 库直接解析 .ppk 文件:
import base64
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
with open("key.ppk") as f:
lines = f.readlines()
# 提取 Private-Lines 的 base64 数据
private_b64 = ""
in_private = False
for line in lines:
line = line.strip()
if line == "Private-Lines: 1":
in_private = True
continue
if in_private and line.startswith("Private-MAC:"):
break
if in_private:
private_b64 += line
raw = base64.b64decode(private_b64)
length = int.from_bytes(raw[:4], "big")
seed = raw[4 : 4 + length]
key = Ed25519PrivateKey.from_private_bytes(seed)
pem = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.OpenSSH,
encryption_algorithm=serialization.NoEncryption(),
)
with open("key_openssh", "wb") as f:
f.write(pem)4. 文件权限
OpenSSH 对私钥文件的权限非常严格。从 .ppk 转换出来的 PEM 文件默认权限太宽松,OpenSSH 会直接拒绝加载:
Bad permissions. Try removing permissions for user: ...
用 PowerShell 修复:
$acl = Get-Acl -LiteralPath "key"
$acl.SetAccessRuleProtection($true, $false)
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
$env:USERNAME, "FullControl", "Allow"
)
$acl.SetAccessRule($rule)
Set-Acl -LiteralPath "key" -AclObject $acl5. SSH 配置
为了避免每次在 URL 中硬编码端口 2222,把配置写进 ~/.ssh/config:
Host gitea.logfun.xyz
HostName gitea.logfun.xyz
Port 2222
User git
IdentityFile "D:/path/to/key"
IdentitiesOnly yes
远程 URL 也从 ssh://git@host:2222/... 改为 SCP 格式 git@host:...,端口由配置文件自动处理。
6. Git SSH 变体
切换 OpenSSH 后拉取,遇到:
fatal: ssh variant 'simple' does not support setting port
Git 默认的 SSH 变体是 simple,不支持在 URL 中解析端口。解决方案是全局配置:
git config --global ssh.variant ssh
git config --global core.sshCommand "ssh -F C:/Users/$USER/.ssh/config"
这样就告诉 Git 使用完整的 OpenSSH 功能,包括端口解析和配置文件。
最终配置
| 项目 | 值 |
|---|---|
| SSH 客户端 | C:\Windows\System32\OpenSSH\ssh.exe |
| 远程 URL | git@gitea.logfun.xyz:user/repo.git |
| 私钥 | OpenSSH PEM 格式(非 .ppk) |
| 端口管理 | ~/.ssh/config |
总结
这次排查涉及了六个独立的环节,任何一个出问题都会导致认证失败:
- 主机密钥缓存 — 首次连接必须确认
- Pageant 冲突 — 运行 Pageant 时 TortoiseGitPlink 的行为会改变
- 密钥格式 —
.ppk和 OpenSSH PEM 不互通 - 文件权限 — OpenSSH 对私钥有严格的 ACL 要求
- SSH 配置 — 用 config 文件管理端口和密钥,而不是 URL
- Git SSH 变体 — 默认
simple模式功能受限
对于 Windows 上的 TortoiseGit 用户,如果使用非标准 SSH 端口(2222、443 等),直接切换到系统 OpenSSH 并通过 ~/.ssh/config 管理配置,比纠结 TortoiseGitPlink + Pageant 要省心得多。