ssh: add ParsePrivateKeysWithPassphrase
ssh package doesn't provide way to parse private keys with passphrase. Fixes golang/go#18692 Change-Id: Ic139f11b6dfe7ef61690d6125e0673d50a48db16 Reviewed-on: https://go-review.googlesource.com/36079 Run-TryBot: Han-Wen Nienhuys <hanwen@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Han-Wen Nienhuys <hanwen@google.com>
This commit is contained in:
committed by
Han-Wen Nienhuys
parent
e7ba826830
commit
fea6c2c835
+43
@@ -756,6 +756,18 @@ func ParsePrivateKey(pemBytes []byte) (Signer, error) {
|
|||||||
return NewSignerFromKey(key)
|
return NewSignerFromKey(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParsePrivateKeyWithPassphrase returns a Signer from a PEM encoded private
|
||||||
|
// key and passphrase. It supports the same keys as
|
||||||
|
// ParseRawPrivateKeyWithPassphrase.
|
||||||
|
func ParsePrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (Signer, error) {
|
||||||
|
key, err := ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewSignerFromKey(key)
|
||||||
|
}
|
||||||
|
|
||||||
// encryptedBlock tells whether a private key is
|
// encryptedBlock tells whether a private key is
|
||||||
// encrypted by examining its Proc-Type header
|
// encrypted by examining its Proc-Type header
|
||||||
// for a mention of ENCRYPTED
|
// for a mention of ENCRYPTED
|
||||||
@@ -790,6 +802,37 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (interface{}, error) {
|
||||||
|
block, _ := pem.Decode(pemBytes)
|
||||||
|
if block == nil {
|
||||||
|
return nil, errors.New("ssh: no key found")
|
||||||
|
}
|
||||||
|
buf := block.Bytes
|
||||||
|
|
||||||
|
if encryptedBlock(block) {
|
||||||
|
if x509.IsEncryptedPEMBlock(block) {
|
||||||
|
var err error
|
||||||
|
buf, err = x509.DecryptPEMBlock(block, passPhrase)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch block.Type {
|
||||||
|
case "RSA PRIVATE KEY":
|
||||||
|
return x509.ParsePKCS1PrivateKey(buf)
|
||||||
|
case "EC PRIVATE KEY":
|
||||||
|
return x509.ParseECPrivateKey(buf)
|
||||||
|
case "DSA PRIVATE KEY":
|
||||||
|
return ParseDSAPrivateKey(buf)
|
||||||
|
case "OPENSSH PRIVATE KEY":
|
||||||
|
return parseOpenSSHPrivateKey(buf)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as
|
// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as
|
||||||
// specified by the OpenSSL DSA man page.
|
// specified by the OpenSSL DSA man page.
|
||||||
func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
|
func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
|
||||||
|
|||||||
@@ -148,6 +148,25 @@ func TestParseEncryptedPrivateKeysFails(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse encrypted private keys with passphrase
|
||||||
|
func TestParseEncryptedPrivateKeysWithPassphrase(t *testing.T) {
|
||||||
|
data := []byte("sign me")
|
||||||
|
for _, tt := range testdata.PEMEncryptedKeys {
|
||||||
|
s, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte(tt.EncryptionKey))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParsePrivateKeyWithPassphrase returned error: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sig, err := s.Sign(rand.Reader, data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("dsa.Sign: %v", err)
|
||||||
|
}
|
||||||
|
if err := s.PublicKey().Verify(data, sig); err != nil {
|
||||||
|
t.Errorf("Verify failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseDSA(t *testing.T) {
|
func TestParseDSA(t *testing.T) {
|
||||||
// We actually exercise the ParsePrivateKey codepath here, as opposed to
|
// We actually exercise the ParsePrivateKey codepath here, as opposed to
|
||||||
// using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
|
// using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
|
||||||
|
|||||||
Reference in New Issue
Block a user