aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2017-04-21 13:33:48 +0200
committerMáximo Cuadros <mcuadros@gmail.com>2017-04-21 13:33:48 +0200
commitc22467ee02bea48f6b173536dca822001b1f3f31 (patch)
tree849bccd7db4c353d2549dc8c30c131756a40e59b
parentd32489902e86c6b667bbc4d28558ebd40a80cf4a (diff)
downloadgo-git-c22467ee02bea48f6b173536dca822001b1f3f31.tar.gz
transport: ssh, NewPublicKeys support for encrypted PEM files
-rw-r--r--plumbing/transport/ssh/auth_method.go33
-rw-r--r--plumbing/transport/ssh/auth_method_test.go11
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)
}