golang.org/x/crypto/ssh (Go)Go
错误信息
ssh: this private key is passphrase protected
也可能输出为
- ssh: no key found
“ssh: no key found”(列为别名)是同一个解析器报告的另一种情况:字节中没有 PEM 密钥块——例如公钥、PuTTY 的 `.ppk` 文件或空文件。
golang.org/x/crypto/ssh (Go) — *ssh.PassphraseMissingError from ssh.ParsePrivateKey
含义
`ssh.ParsePrivateKey` 识别出这是一个加密的私钥,于是停了下来,因为它没有用来解密的密码短语。返回的 `*ssh.PassphraseMissingError` 甚至携带了公钥,但签名需要解密后的私钥:调用 `ssh.ParsePrivateKeyWithPassphrase`,或者让 SSH 代理完成签名。与之相近的“ssh: no key found”则表示输入中根本没有可识别的密钥。
原因
密钥在创建时设置了密码短语
常见`ssh-keygen` 会提示输入密码短语,并用它加密密钥文件。交互式的 OpenSSH 会询问密码短语或使用 SSH 代理;Go 程序则必须显式提供它。
工具以为 SSH 代理会解锁密钥
偶见在工作站上,`ssh-add` 持有解密后的密钥,`ssh` 从不读取密钥文件。直接读取文件的 Go 代码则用不上 SSH 代理。
把个人的加密密钥复制到了 CI 中
偶见用于自动化的部署密钥通常不加密,权限范围也很窄;在 CI 中复用个人的加密密钥会在这里失败。
修复方法
- 1.
检测该错误并提供密码短语
`errors.As` 可以识别出缺少密码短语的错误;从机密存储中读取密码短语,切勿从源代码中读取。
go signer, err := ssh.ParsePrivateKey(keyBytes) var missing *ssh.PassphraseMissingError if errors.As(err, &missing) { passphrase := []byte(os.Getenv("SSH_KEY_PASSPHRASE")) signer, err = ssh.ParsePrivateKeyWithPassphrase(keyBytes, passphrase) } if err != nil { return err } - 2.
通过 SSH 代理签名
使用 `golang.org/x/crypto/ssh/agent` 时,私钥永远不会进入你的进程,密码短语也由 SSH 代理处理。
go import "golang.org/x/crypto/ssh/agent" conn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) if err != nil { return err } agentClient := agent.NewClient(conn) auth := ssh.PublicKeysCallback(agentClient.Signers) // the agent holds the decrypted key