【问题标题】:How to Generate ECDSA Key Pair for SSH in Go?如何在 Go 中为 SSH 生成 ECDSA 密钥对?
【发布时间】:2022-01-18 00:24:43
【问题描述】:

我正在尝试使用 Go 为 SSH 生成 ECDSA 密钥对,但我发现私钥格式与 ssh-keygen 不同,并且无法被 GitHub 接受。

这是通过ssh-keygen -t ecdsa -b 256 生成的 256 位密钥对:

ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOJWUhO+waiB+aKvXO0xC5XTL6P/X/TIzQ4hgdXkDfmAfntOj/HXRIu4GulCvJgUoyTiF5Qt9j9gK6Z17szUv3s= root@9e5eef4b58c5

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTiVlITvsGogfmir1ztMQuV0y+j/1/0
yM0OIYHV5A35gH57To/x10SLuBrpQryYFKMk4heULfY/YCumde7M1L97AAAAsDs42wc7ON
sHAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOJWUhO+waiB+aKv
XO0xC5XTL6P/X/TIzQ4hgdXkDfmAfntOj/HXRIu4GulCvJgUoyTiF5Qt9j9gK6Z17szUv3
sAAAAgOpAIXW6rZQqgYZboSJXojH2diS26wfm6P3hn8cQVZrwAAAARcm9vdEA5ZTVlZWY0
YjU4YzUBAgMEBQYH
-----END OPENSSH PRIVATE KEY-----

这是 Go 中的生成器代码:

// GenerateECDSAKeys generates ECDSA public and private key pair with given size for SSH.
func GenerateECDSAKeys(bitSize int) (pubKey string, privKey string, err error) {
    // generate private key
    var privateKey *ecdsa.PrivateKey
    if privateKey, err = ecdsa.GenerateKey(curveFromLength(bitSize), rand.Reader); err != nil {
        return
    }

    // encode public key
    var (
        bytes     []byte
        publicKey ssh.PublicKey
    )
    if publicKey, err = ssh.NewPublicKey(privateKey.Public()); err != nil {
        return
    }
    pubBytes := ssh.MarshalAuthorizedKey(publicKey)

    // encode private key
    if bytes, err = x509.MarshalECPrivateKey(privateKey); err != nil {
        return
    }
    privBytes := pem.EncodeToMemory(&pem.Block{
        Type:  "ECDSA PRIVATE KEY",
        Bytes: bytes,
    })

    return string(pubBytes), string(privBytes), nil
}

func curveFromLength(l int) elliptic.Curve {
    switch l {
    case 224:
        return elliptic.P224()
    case 256:
        return elliptic.P256()
    case 348:
        return elliptic.P384()
    case 521:
        return elliptic.P521()
    }
    return elliptic.P384()
}

并生成结果:

ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNYTtNRlEKh/harLSfIsSziDkEQ8E7OJ7azhTBJi1Qx+fDa6dGg9f/vudGEizJ5d9TINVLTP+Jemwg6FBhajiVA=

-----BEGIN ECDSA PRIVATE KEY-----
MHcCAQEEIEGZZ/4aD6tf0sc1ovyctlGWRSFp7RGw5ovRONZKLg4eoAoGCCqGSM49
AwEHoUQDQgAE1hO01GUQqH+FqstJ8ixLOIOQRDwTs4ntrOFMEmLVDH58Nrp0aD1/
++50YSLMnl31Mg1UtM/4l6bCDoUGFqOJUA==
-----END ECDSA PRIVATE KEY-----

那么为什么生成后SSH私钥如此不同,又是如何让它发挥作用的呢?

【问题讨论】:

  • OpenSSH 使用 SEC1 格式和 OpenSSH 格式作为 EC 私钥,详情请参阅 here。 Go 代码生成一个 SEC1 格式的私有 EC 密钥。但是,页眉和页脚是错误的,它们实际上包含 EC 而不是 ECDSA,即将Type: "ECDSA PRIVATE KEY" 替换为Type: "EC PRIVATE KEY"。也许那时会接受 SEC1 密钥。如果没有,您可能需要一个支持较新 OpenSSH 格式的 Go 库。请注意,您可以在格式之间进行转换,例如ssh-keygen。
  • 顺便说一句,您拼错了 384,并且 SSH 不允许 P-224,因此生成它没有意义。否则同意@Topaco。几乎crossdupe security.stackexchange.com/questions/200935/… 并在那里链接security.stackexchange.com/questions/39279/…
  • @dave_thompson_085 感谢您的指出和参考!但是我实际上使用了 P-256。
  • @Topaco 谢谢!我没有意识到标题很重要。替换后,Go 生成的 SSH 密钥适用于 GitHub Push/Pull 和 SSH Login。你介意写一个详细的答案并解释标题,所以我可以接受它作为答案吗?谢谢!
  • @CeciliaWong - 不客气,我已经发布了答案。

标签: go ssh cryptography rsa ecdsa


【解决方案1】:

OpenSSH 对私有 EC 密钥使用不同的格式,SEC1(由您的 Go 代码生成)、PKCS#8 或更新的 OpenSSH 格式(由 ssh-keygen 命令生成)。这在here 中进行了描述,其中还包含对 OpenSSH 格式的更详细说明。 SEC1 格式被解释为例如在这个post

当前的 Go 代码生成一个带有 错误页眉和页脚的 SEC1 密钥。这原来是问题的原因!要修复该错误,必须将页眉和页脚中的ECDSA 替换为EC

-----BEGIN EC PRIVATE KEY-----
...
-----END EC PRIVATE KEY-----

即在 EncodeToMemory() 调用的 Go 代码中,Type: "ECDSA PRIVATE KEY" 必须替换为 Type: "EC PRIVATE KEY"

请注意,格式之间的转换也是可能的,例如使用 ssh-keygen。比如

ssh-keygen -p -N "" -f data.key

data.key 中包含的SEC1 密钥转换为OpenSSH 格式,请参阅here

【讨论】:

    猜你喜欢
    • 2017-03-02
    • 1970-01-01
    • 2015-04-06
    • 2011-01-28
    • 1970-01-01
    • 2015-09-17
    • 1970-01-01
    • 2015-12-18
    • 1970-01-01
    相关资源
    最近更新 更多