Golang中使用带密码的Ed25519私钥SSH时出现"PassPhraseMissingError"错误
Golang中使用带密码的Ed25519私钥SSH时出现"PassPhraseMissingError"错误
1. 您使用的 Go 版本是什么(go version)?
go1.16.2
2. 您使用的操作系统和处理器架构是什么?
windows/amd64
3. 您做了什么?
运行 go-git-clone.go 两次:
第一次使用由 ssh-keygen -t rsa -b 3072 生成的受密码保护的密钥。
第二次使用由 ssh-keygen -t ed25519 生成的受密码保护的密钥。
每次运行命令为:
git_clone_go.exe <git server> <local dir> <path to keyfile> <password>
使用的 ssh-keygen 版本来自:C:\WINDOWS\System32\OpenSSH\ssh-keygen.exe,版本 7.6.0.0
RSA 密钥被正确解密并使用。
ed25519 密钥抛出错误:
generate publickeys failed: ssh: this private key is passphrase protected
4. 您期望看到什么?
应该能够像使用 RSA 密钥一样,使用带密码的 ed25519 密钥。
5. 您实际看到了什么?
输出:
generate publickeys failed: ssh: this private key is passphrase protected
输出信息中的 ssh: this private key is passphrase protected 部分是由 golang.org/x/crypto/ssh/keys.go 文件中的 func unencryptedOpenSSHKey 函数里的 “PassPhraseMissingError” 消息生成的。
func ParseRawPrivateKey 函数走了 case "OPENSSH PRIVATE KEY": 这个分支。
更多关于Golang中使用带密码的Ed25519私钥SSH时出现"PassPhraseMissingError"错误的实战教程也可以访问 https://www.itying.com/category-94-b0.html
go-git-clone.go 可以在以下位置找到:
https://github.com/go-git/go-git/blob/master/_examples/clone/auth/basic/access_token/main.go
更多关于Golang中使用带密码的Ed25519私钥SSH时出现"PassPhraseMissingError"错误的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
问题出在Go的x/crypto/ssh包对加密的OpenSSH格式Ed25519私钥支持不完整。当使用ParseRawPrivateKey解析带密码的Ed25519密钥时,会返回PassPhraseMissingError错误,因为该函数没有实现解密逻辑。
以下是解决方案的示例代码:
package main
import (
"bytes"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
// 自定义函数处理加密的OpenSSH私钥
func parseEncryptedPrivateKey(data []byte, passphrase []byte) (interface{}, error) {
// 尝试解析为PEM格式
block, _ := pem.Decode(data)
if block != nil {
// 如果是加密的PEM格式
if x509.IsEncryptedPEMBlock(block) {
decrypted, err := x509.DecryptPEMBlock(block, passphrase)
if err != nil {
return nil, err
}
return ssh.ParseRawPrivateKey(decrypted)
}
return ssh.ParseRawPrivateKey(data)
}
// 处理OpenSSH格式的加密密钥
if bytes.Contains(data, []byte("OPENSSH PRIVATE KEY")) {
// 使用ssh-agent兼容的方式解密
key, err := ssh.ParseRawPrivateKeyWithPassphrase(data, passphrase)
if err != nil {
return nil, fmt.Errorf("failed to parse encrypted openssh key: %w", err)
}
return key, nil
}
return nil, errors.New("unsupported key format")
}
// 替代方案:使用ssh-agent
func loadEncryptedKeyWithAgent(keyPath string, passphrase []byte) (ssh.Signer, error) {
keyData, err := ioutil.ReadFile(keyPath)
if err != nil {
return nil, err
}
// 创建内存中的ssh-agent
ag := agent.NewKeyring()
// 使用agent.Add方法添加加密的密钥
addedKey := agent.AddedKey{
PrivateKey: keyData,
Comment: "",
LifetimeSecs: 0,
}
// 对于加密的密钥,需要设置解密函数
signer, err := ssh.ParsePrivateKeyWithPassphrase(keyData, passphrase)
if err != nil {
return nil, err
}
// 添加到agent
if err := ag.Add(agent.AddedKey{
PrivateKey: signer,
Comment: "",
LifetimeSecs: 0,
}); err != nil {
return nil, err
}
return signer, nil
}
// 主函数示例
func main() {
keyPath := "path/to/ed25519_key"
passphrase := []byte("your_password")
// 方法1:使用自定义解析函数
keyData, err := ioutil.ReadFile(keyPath)
if err != nil {
panic(err)
}
privateKey, err := parseEncryptedPrivateKey(keyData, passphrase)
if err != nil {
// 方法2:使用ssh-agent方式
signer, err := loadEncryptedKeyWithAgent(keyPath, passphrase)
if err != nil {
panic(err)
}
// 使用signer进行SSH认证
config := &ssh.ClientConfig{
User: "git",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// 连接SSH服务器
client, err := ssh.Dial("tcp", "github.com:22", config)
if err != nil {
panic(err)
}
defer client.Close()
fmt.Println("SSH connection established")
}
}
对于go-git库,可以使用以下方式:
import (
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
)
func cloneWithEncryptedEd25519() error {
publicKeys, err := ssh.NewPublicKeysFromFile("git", "path/to/ed25519_key", "password")
if err != nil {
// 如果上述方法失败,使用自定义的AuthMethod
auth, err := createCustomAuth("path/to/ed25519_key", "password")
if err != nil {
return err
}
_, err = git.PlainClone("/path/to/repo", false, &git.CloneOptions{
URL: "git@github.com:user/repo.git",
Auth: auth,
Progress: os.Stdout,
})
return err
}
_, err = git.PlainClone("/path/to/repo", false, &git.CloneOptions{
URL: "git@github.com:user/repo.git",
Auth: publicKeys,
Progress: os.Stdout,
})
return err
}
func createCustomAuth(keyPath, password string) (ssh.AuthMethod, error) {
keyData, err := ioutil.ReadFile(keyPath)
if err != nil {
return nil, err
}
signer, err := ssh.ParsePrivateKeyWithPassphrase(keyData, []byte(password))
if err != nil {
return nil, err
}
return ssh.PublicKeys(signer), nil
}
注意:这个问题在Go 1.16中确实存在,因为x/crypto/ssh包对加密的OpenSSH格式Ed25519密钥支持有限。建议升级到更新的Go版本,或者使用上述的变通方案。

