英文:
How to Generate ECDSA Key Pair for SSH in Go?
问题
我正在尝试使用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私钥与之前不同,如何使其正常工作?
英文:
I'm trying to generate ECDSA Key Pair for SSH with Go, but I find that the private key format is different from ssh-keygen
and can't be accepted by GitHub.
Here's the 256-bit key pair generated via ssh-keygen -t ecdsa -b 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-----
Here's the generator code in 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()
}
And generated result:
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNYTtNRlEKh/harLSfIsSziDkEQ8E7OJ7azhTBJi1Qx+fDa6dGg9f/vudGEizJ5d9TINVLTP+Jemwg6FBhajiVA=
-----BEGIN ECDSA PRIVATE KEY-----
MHcCAQEEIEGZZ/4aD6tf0sc1ovyctlGWRSFp7RGw5ovRONZKLg4eoAoGCCqGSM49
AwEHoUQDQgAE1hO01GUQqH+FqstJ8ixLOIOQRDwTs4ntrOFMEmLVDH58Nrp0aD1/
++50YSLMnl31Mg1UtM/4l6bCDoUGFqOJUA==
-----END ECDSA PRIVATE KEY-----
So why SSH private key was so different after the generation, and how to make it works?
答案1
得分: 2
OpenSSH使用不同的格式来存储私钥,包括SEC1格式(由您的Go代码生成)、PKCS#8格式和较新的OpenSSH格式(使用ssh-keygen命令生成)。这在这里有详细描述,其中还包含了对OpenSSH格式的更详细解释。SEC1格式在这个帖子中有解释。
当前的Go代码生成了一个带有错误头部和尾部的SEC1密钥。这被证明是问题的原因!为了修复这个错误,在头部和尾部中必须将ECDSA
替换为EC
:
-----BEGIN EC PRIVATE KEY-----
...
-----END EC PRIVATE KEY-----
也就是说,在Go代码的EncodeToMemory()
调用中,Type: "ECDSA PRIVATE KEY"
必须替换为Type: "EC PRIVATE KEY"
。
请注意,可以使用ssh-keygen进行格式之间的转换。例如,
ssh-keygen -p -N "" -f data.key
将data.key
中包含的SEC1密钥转换为OpenSSH格式,参见这里。
英文:
OpenSSH uses different formats for private EC keys, the SEC1 (as generated by your Go code), the PKCS#8 or the newer OpenSSH format (as generated with the ssh-keygen command). This is described here, which also contains a more detailed explanation of the OpenSSH format. The SEC1 format is explained e.g. in this post.
The current Go code generates a SEC1 key with wrong header and footer. This turned out to be the cause of the problem! To fix the bug, ECDSA
must be replaced by EC
in header and footer:
-----BEGIN EC PRIVATE KEY-----
...
-----END EC PRIVATE KEY-----
i.e. in the Go code in the EncodeToMemory()
call Type: "ECDSA PRIVATE KEY"
must be replaced by Type: "EC PRIVATE KEY"
.
Note that a conversion between the formats is also possible e.g. with ssh-keygen. For instance
ssh-keygen -p -N "" -f data.key
converts the SEC1 key contained in data.key
to the OpenSSH format, see here.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论