【问题标题】:Generate ed25519 key-pair compatible with openssh生成与 openssh 兼容的 ed25519 密钥对
【发布时间】:2022-07-14 06:29:53
【问题描述】:

我想在 go 中使用 ed25519 生成与 openssh 兼容的 ssh 密钥来替换 rsa.GenerateKey 因为 github 不再支持它了。

应该相当于:

ssh-keygen -t ed25519 -C "your_email@example.com"

但我找不到办法。

现在,我有这个代码:

func GenerateSSHKeys() (*ED25519Keys, error) {
    publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
    if err != nil {
        return nil, err
    }

    publicED25519Key, err := ssh.NewPublicKey(publicKey)

    if err != nil {
        return nil, err
    }

    pubKeyBytes := ssh.MarshalAuthorizedKey(publicED25519Key)

    bytes, err := x509.MarshalPKCS8PrivateKey(privateKey) 
    if err != nil {
        return nil, err
    }

    privBlock := pem.Block{
        Type:    "PRIVATE KEY",
        Headers: nil,
        Bytes:   bytes,
    }

    privatePEM := pem.EncodeToMemory(&privBlock)

    return &ED25519Keys{
        Public:  pubKeyBytes,
        Private: privatePEM,
    }, nil

}

但似乎私钥更短,我无法解释我在 git 或 argocd 中使用它时的一些奇怪行为(有时它有效,但大多数时候无效)。

-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEINV+5Hyey1xTblwsVGfGmDCMdZgKQdhf1ublkGO2Qaf+
-----END PRIVATE KEY-----

我怎么能得到这样的结果:

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAxIu+ndqJXpEJLk5c2qsjPvUybP8OANZlSqLaOau9ZCQAAAKCocC5dqHAu
[...]
AAAEChVq8FJPCYbKnNFFuISac83mzF+DDFCDrLd9Xva9fQ2zEi76d2olekQkuTlzaqyM+9
TJs/w4A1mVKoto5q71kJAAAAFnlvdXJfZW1haWxAZXhhbXBsZS5jb20BAgMEBQYH
-----END OPENSSH PRIVATE KEY-----

【问题讨论】:

    标签: go openssh


    【解决方案1】:

    是的,我也遇到过。

    x509 包不支持以openssh 使用的格式编组ed25519 密钥类型,因此正如您所发现的,此代码(适用于其他密钥类型)对于ed25519 密钥失败:

    bytes, err := x509.MarshalPKCS8PrivateKey(privateKey)  // produces invalid output for ed25519 keys
    

    有一个带有帮助函数 edkey.MarshalED25519PrivateKey 的 repo (github.com/mikesmitty/edkey) 来解决这个问题:

    /* 将 ed25519 私钥写入新的 OpenSSH 私钥 格式。我不知道为什么这还没有在任何地方实施,你 除了在 OpenSSH 中将其写入磁盘之外,似乎可以做任何事情 私钥格式。 */

    好像是仿照openssh源码:sshkey.csshkey_private_to_blob2

    因此,要么将该辅助函数复制到您的代码中(推荐因为 repo 已有 5 年以上的历史),要么将其作为导入引用:

    import "github.com/mikesmitty/edkey"
    
    pubKey, privKey, _ := ed25519.GenerateKey(rand.Reader)
    publicKey, _ := ssh.NewPublicKey(pubKey)
    
    pemKey := &pem.Block{
        Type:  "OPENSSH PRIVATE KEY",
        Bytes: edkey.MarshalED25519PrivateKey(privKey),  // <- marshals ed25519 correctly
    }
    privateKey := pem.EncodeToMemory(pemKey)
    authorizedKey := ssh.MarshalAuthorizedKey(publicKey)
    
    _ = ioutil.WriteFile("id_ed25519", privateKey, 0600)
    _ = ioutil.WriteFile("id_ed25519.pub", authorizedKey, 0644)
    

    【讨论】: