Golang SSH密钥包不匹配的问题排查:密钥正确但验证失败
Golang SSH密钥包不匹配的问题排查:密钥正确但验证失败
你好,能否帮我找出密钥不匹配的原因?我使用的是 golang.org/x/crypto/ssh 包,执行代码时显示:
ssh: handshake failed: ssh: host key mismatch
我已经重新检查了主机和服务器的密钥文件,并且通过终端连接也没有问题。
我检查了 getHostKey() 函数返回的内容,结果发现,将字节转换为字符串时:
fmt.Println(string(hostKey.Marshal()))
它显示为:
ecdsa-sha2-nistp25nistp256A- “T�.��2l�IR�” �mDĄ���F���7dɇ� # a��: ���YP�9�� ��-I ^ �c�
这可能是由于密钥没有以正确的编码显示,因此导致不匹配。如果是这样,该如何修复?
以下是完整的代码:
func getHostKey(host string) ssh.PublicKey {
// parse OpenSSH known_hosts file
// ssh or use ssh-keyscan to get initial key
file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
var hostKey ssh.PublicKey
for scanner.Scan() {
fields := strings.Split(scanner.Text(), " ")
if len(fields) != 3 {
continue
}
if strings.Contains(fields[0], host) {
var err error
hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
if err != nil {
log.Fatalf("error parsing %q: %v", fields[2], err)
}
break
}
}
if hostKey == nil {
log.Fatalf("no hostkey found for %s", host)
}
return hostKey
}
func main() {
host := "192.168.128.193"
port := "22"
user := "n0kk"
pass := "password"
cmd := "ps"
// get host public key
hostKey := getHostKey(host)
fmt.Println(string(hostKey.Marshal()))
// ssh client config
config := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.Password(pass),
},
// allow any host key to be used (non-prod)
// HostKeyCallback: ssh.InsecureIgnoreHostKey(),
// verify host public key
HostKeyCallback: ssh.FixedHostKey(hostKey),
// optional host key algo list
HostKeyAlgorithms: []string{
ssh.KeyAlgoRSA,
ssh.KeyAlgoDSA,
ssh.KeyAlgoECDSA256,
ssh.KeyAlgoECDSA384,
ssh.KeyAlgoECDSA521,
ssh.KeyAlgoED25519,
},
// optional tcp connect timeout
Timeout: 5 * time.Second,
}
// connect
client, err := ssh.Dial("tcp", host+":"+port, config)
if err != nil {
log.Fatal(err)
}
defer client.Close()
// start session
sess, err := client.NewSession()
if err != nil {
log.Fatal(err)
}
defer sess.Close()
// setup standard out and error
// uses writer interface
sess.Stdout = os.Stdout
sess.Stderr = os.Stderr
// run single command
err = sess.Run(cmd)
if err != nil {
log.Fatal(err)
}
}
更多关于Golang SSH密钥包不匹配的问题排查:密钥正确但验证失败的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang SSH密钥包不匹配的问题排查:密钥正确但验证失败的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
问题出在getHostKey()函数中解析known_hosts文件的方式。当使用strings.Contains(fields[0], host)匹配主机时,如果known_hosts文件中有多个条目包含该IP地址(比如同时有192.168.128.193和192.168.128.193,example.com),可能会匹配到错误的条目。
更可靠的方法是使用ssh.ParseKnownHosts()函数,它会正确处理known_hosts文件的格式。以下是修复后的代码:
func getHostKey(host string) ssh.PublicKey {
knownHostsPath := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")
// 使用ssh.ParseKnownHosts正确解析known_hosts文件
file, err := os.Open(knownHostsPath)
if err != nil {
log.Fatal(err)
}
defer file.Close()
// 读取文件内容
content, err := io.ReadAll(file)
if err != nil {
log.Fatal(err)
}
// 解析所有已知主机
knownHosts, err := ssh.ParseKnownHosts(content)
if err != nil {
log.Fatal(err)
}
// 查找匹配的主机密钥
for _, knownHost := range knownHosts {
for _, knownHostPattern := range knownHost.Patterns {
// 精确匹配主机名或IP
if knownHostPattern == host {
return knownHost.Key
}
// 如果known_hosts中使用的是逗号分隔的主机列表
hosts := strings.Split(knownHostPattern, ",")
for _, h := range hosts {
if h == host {
return knownHost.Key
}
}
}
}
log.Fatalf("no hostkey found for %s", host)
return nil
}
或者,更简单的方法是直接使用ssh.KnownHosts()作为HostKeyCallback:
func main() {
host := "192.168.128.193"
port := "22"
user := "n0kk"
pass := "password"
cmd := "ps"
knownHostsPath := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")
// 使用ssh.KnownHosts自动处理known_hosts文件
hostKeyCallback, err := ssh.KnownHosts(knownHostsPath)
if err != nil {
log.Fatal(err)
}
config := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.Password(pass),
},
HostKeyCallback: hostKeyCallback,
HostKeyAlgorithms: []string{
ssh.KeyAlgoRSA,
ssh.KeyAlgoDSA,
ssh.KeyAlgoECDSA256,
ssh.KeyAlgoECDSA384,
ssh.KeyAlgoECDSA521,
ssh.KeyAlgoED25519,
},
Timeout: 5 * time.Second,
}
client, err := ssh.Dial("tcp", host+":"+port, config)
if err != nil {
log.Fatal(err)
}
defer client.Close()
sess, err := client.NewSession()
if err != nil {
log.Fatal(err)
}
defer sess.Close()
sess.Stdout = os.Stdout
sess.Stderr = os.Stderr
err = sess.Run(cmd)
if err != nil {
log.Fatal(err)
}
}
如果问题仍然存在,可以添加调试代码查看实际接收到的密钥:
// 调试用:打印密钥指纹
config.HostKeyCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error {
fmt.Printf("Host: %s\n", hostname)
fmt.Printf("Key type: %s\n", key.Type())
fmt.Printf("Key fingerprint: %s\n", ssh.FingerprintSHA256(key))
// 调用原始的回调函数
return hostKeyCallback(hostname, remote, key)
}
这样可以看到实际接收到的密钥指纹,与known_hosts文件中的指纹进行对比。

