diff options
author | Máximo Cuadros <mcuadros@gmail.com> | 2017-04-21 13:33:48 +0200 |
---|---|---|
committer | Máximo Cuadros <mcuadros@gmail.com> | 2017-04-21 13:33:48 +0200 |
commit | c22467ee02bea48f6b173536dca822001b1f3f31 (patch) | |
tree | 849bccd7db4c353d2549dc8c30c131756a40e59b | |
parent | d32489902e86c6b667bbc4d28558ebd40a80cf4a (diff) | |
download | go-git-c22467ee02bea48f6b173536dca822001b1f3f31.tar.gz |
transport: ssh, NewPublicKeys support for encrypted PEM files
-rw-r--r-- | plumbing/transport/ssh/auth_method.go | 33 | ||||
-rw-r--r-- | plumbing/transport/ssh/auth_method_test.go | 11 |
2 files changed, 35 insertions, 9 deletions
diff --git a/plumbing/transport/ssh/auth_method.go b/plumbing/transport/ssh/auth_method.go index ad92ee1..a3e1ad1 100644 --- a/plumbing/transport/ssh/auth_method.go +++ b/plumbing/transport/ssh/auth_method.go @@ -1,6 +1,8 @@ package ssh import ( + "crypto/x509" + "encoding/pem" "errors" "fmt" "io/ioutil" @@ -9,9 +11,11 @@ import ( "os/user" "path/filepath" - "github.com/src-d/crypto/ssh/knownhosts" + "gopkg.in/src-d/go-git.v4/plumbing/transport" + "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" + "golang.org/x/crypto/ssh/knownhosts" ) const DefaultUsername = "git" @@ -22,6 +26,7 @@ var ErrEmptySSHAgentAddr = errors.New("SSH_AUTH_SOCK env variable is required") // must implement. The clientConfig method returns the ssh client // configuration needed to establish an ssh connection. type AuthMethod interface { + transport.AuthMethod clientConfig() *ssh.ClientConfig hostKeyCallback() (ssh.HostKeyCallback, error) } @@ -112,9 +117,22 @@ type PublicKeys struct { baseAuthMethod } -// NewPublicKeys returns a PublicKeys from a PEM encoded private key. It -// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys. -func NewPublicKeys(user string, pemBytes []byte) (AuthMethod, error) { +// NewPublicKeys returns a PublicKeys from a PEM encoded private key. An +// encryption password should be given if the pemBytes contains a password +// encrypted PEM block otherwise password should be empty. It supports RSA +// (PKCS#1), DSA (OpenSSL), and ECDSA private keys. +func NewPublicKeys(user string, pemBytes []byte, password string) (AuthMethod, error) { + block, _ := pem.Decode(pemBytes) + if x509.IsEncryptedPEMBlock(block) { + key, err := x509.DecryptPEMBlock(block, []byte(password)) + if err != nil { + return nil, err + } + + block = &pem.Block{Type: block.Type, Bytes: key} + pemBytes = pem.EncodeToMemory(block) + } + signer, err := ssh.ParsePrivateKey(pemBytes) if err != nil { return nil, err @@ -124,14 +142,15 @@ func NewPublicKeys(user string, pemBytes []byte) (AuthMethod, error) { } // NewPublicKeysFromFile returns a PublicKeys from a file containing a PEM -// encoded private key. -func NewPublicKeysFromFile(user string, pemFile string) (AuthMethod, error) { +// encoded private key. An encryption password should be given if the pemBytes +// contains a password encrypted PEM block otherwise password should be empty. +func NewPublicKeysFromFile(user, pemFile, password string) (AuthMethod, error) { bytes, err := ioutil.ReadFile(pemFile) if err != nil { return nil, err } - return NewPublicKeys(user, bytes) + return NewPublicKeys(user, bytes, password) } func (a *PublicKeys) Name() string { diff --git a/plumbing/transport/ssh/auth_method_test.go b/plumbing/transport/ssh/auth_method_test.go index 43b34df..6f3d82f 100644 --- a/plumbing/transport/ssh/auth_method_test.go +++ b/plumbing/transport/ssh/auth_method_test.go @@ -109,7 +109,14 @@ func (s *SuiteCommon) TestNewSSHAgentAuth(c *C) { } func (*SuiteCommon) TestNewPublicKeys(c *C) { - auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"]) + auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"], "") + c.Assert(err, IsNil) + c.Assert(auth, NotNil) +} + +func (*SuiteCommon) TestNewPublicKeysWithEncryptedPEM(c *C) { + f := testdata.PEMEncryptedKeys[0] + auth, err := NewPublicKeys("foo", f.PEMBytes, f.EncryptionKey) c.Assert(err, IsNil) c.Assert(auth, NotNil) } @@ -122,7 +129,7 @@ func (*SuiteCommon) TestNewPublicKeysFromFile(c *C) { c.Assert(f.Close(), IsNil) defer os.RemoveAll(f.Name()) - auth, err := NewPublicKeysFromFile("foo", f.Name()) + auth, err := NewPublicKeysFromFile("foo", f.Name(), "") c.Assert(err, IsNil) c.Assert(auth, NotNil) } |