如何在golang ssh中使用加密的私钥

16

我希望得到指引,因为我不知道如何解密加密的密钥,以便在golang ssh中使用它。我试图将两个其他代码来源(包括这个)组合起来,但无法使其工作。

我认为我已经获得了DER格式的密钥,但需要将其转换为PEM格式,以便与crypto/ssh一起使用。

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,D7C72273BE168626E5B2D1BC72E56326
...
-----END RSA PRIVATE KEY-----

我读到了它:

key, err := ioutil.ReadFile(privateKey)
if err != nil {
    log.Fatalf("Unable to read private key: %v", err)
}

有了一个未加密的(!) 密钥,我可以:

signer, err := ssh.ParsePrivateKey(key)
if err != nil {
    log.Fatalf("Unable to parse private key: %v", err)
}

config := &ssh.ClientConfig{
    User: username,
    Auth: []ssh.AuthMethod{
        ssh.PublicKeys(signer),
    },
}

这将能够起作用。

我重复使用了一些代码,我认为这些代码可以将 PEM 解密为 DER 格式:

func decrypt(key []byte, password []byte) []byte {
    block, rest := pem.Decode(key)
    if len(rest) > 0 {
        log.Fatalf("Extra data included in key")
    }
    der, err := x509.DecryptPEMBlock(block, password)
    if err != nil {
        log.Fatalf("Decrypt failed: %v", err)
    }
    return der
}

那么,我该如何从DER获取签名者呢?

或者说,最佳解决方案是什么?

3个回答

20
import "golang.org/x/crypto/ssh"

使用未加密的密钥:

signer, err := ssh.ParsePrivateKey(key)

使用加密密钥:

signer, err := ssh.ParsePrivateKeyWithPassphrase(key, []byte("password"))

那么:

config := &ssh.ClientConfig{
    User: username,
    Auth: []ssh.AuthMethod{
        ssh.PublicKeys(signer),
    },
}

也许ParsePrivateKeyWithPassphrase()是该软件包的一个新添加功能。如果您使用正确的软件包名称“ssh”而不是“ssh2”,则可以使用此功能。 - jaeheung
为什么Auth接受AuthMethod{PublicKey}?SSH客户端使用私钥... - undefined

10
如果你拥有带有RSA私钥的DER块,你需要使用x509.ParsePKCS1PrivateKey来解析该密钥,并使用ssh.NewSignerFromKey获取ssh.Signer
key, err := x509.ParsePKCS1PrivateKey(der)
if err != nil {
    log.Fatal(err)
}
signer := ssh.NewSignerFromKey(key)

太棒了。非常感谢! - DazWilkin

7

我这里提供一个备选方案,可以重复使用 ssh.ParsePrivateKey(key)。我修改了 decrypt 函数以解密并编码私钥(如果它被加密),然后将其返回,这样返回的 key 可以直接用于 ssh.ParsePrivateKey(key)。它利用 pem.EncodeToMemory 从已解密的 PEM 块中获取密钥。

func decrypt(key []byte, password []byte) []byte {
    block, rest := pem.Decode(key)
    if len(rest) > 0 {
        log.Fatalf("Extra data included in key")
    }

    if x509.IsEncryptedPEMBlock(block) {
        der, err := x509.DecryptPEMBlock(block, password)
        if err != nil {
            log.Fatalf("Decrypt failed: %v", err)
        }
        return pem.EncodeToMemory(&pem.Block{Type: block.Type, Bytes: der})
    }
    return key
}

1
我会将硬编码的 {Type: "RSA PRIVATE KEY", ...} 更改为 {Type: block.Type, ...},因为如果输入块不是类型为“RSA PRIVATE KEY”,则此代码片段将意外更改PEM块类型。 - joonas.fi
1
谢谢!对答案进行了修改。 - Madis
2
x509.IsEncryptedPEMBlock和x509.DecryptPEMBlock被标记为过时且设计上存在不安全因素。 - Karel Bílek
@KarelBílek 我也看到了这个问题,你有没有其他的解决方法?如果有,能否指点我正确的方向? - Izio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接