Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit 9e4f992

Browse files
authored
Merge pull request #355 from mcuadros/ssh-encrypted
transport: ssh, NewPublicKeys support for encrypted PEM files
2 parents d324899 + c22467e commit 9e4f992

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed

plumbing/transport/ssh/auth_method.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package ssh
22

33
import (
4+
"crypto/x509"
5+
"encoding/pem"
46
"errors"
57
"fmt"
68
"io/ioutil"
@@ -9,9 +11,11 @@ import (
911
"os/user"
1012
"path/filepath"
1113

12-
"github.com/src-d/crypto/ssh/knownhosts"
14+
"gopkg.in/src-d/go-git.v4/plumbing/transport"
15+
1316
"golang.org/x/crypto/ssh"
1417
"golang.org/x/crypto/ssh/agent"
18+
"golang.org/x/crypto/ssh/knownhosts"
1519
)
1620

1721
const DefaultUsername = "git"
@@ -22,6 +26,7 @@ var ErrEmptySSHAgentAddr = errors.New("SSH_AUTH_SOCK env variable is required")
2226
// must implement. The clientConfig method returns the ssh client
2327
// configuration needed to establish an ssh connection.
2428
type AuthMethod interface {
29+
transport.AuthMethod
2530
clientConfig() *ssh.ClientConfig
2631
hostKeyCallback() (ssh.HostKeyCallback, error)
2732
}
@@ -112,9 +117,22 @@ type PublicKeys struct {
112117
baseAuthMethod
113118
}
114119

115-
// NewPublicKeys returns a PublicKeys from a PEM encoded private key. It
116-
// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
117-
func NewPublicKeys(user string, pemBytes []byte) (AuthMethod, error) {
120+
// NewPublicKeys returns a PublicKeys from a PEM encoded private key. An
121+
// encryption password should be given if the pemBytes contains a password
122+
// encrypted PEM block otherwise password should be empty. It supports RSA
123+
// (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
124+
func NewPublicKeys(user string, pemBytes []byte, password string) (AuthMethod, error) {
125+
block, _ := pem.Decode(pemBytes)
126+
if x509.IsEncryptedPEMBlock(block) {
127+
key, err := x509.DecryptPEMBlock(block, []byte(password))
128+
if err != nil {
129+
return nil, err
130+
}
131+
132+
block = &pem.Block{Type: block.Type, Bytes: key}
133+
pemBytes = pem.EncodeToMemory(block)
134+
}
135+
118136
signer, err := ssh.ParsePrivateKey(pemBytes)
119137
if err != nil {
120138
return nil, err
@@ -124,14 +142,15 @@ func NewPublicKeys(user string, pemBytes []byte) (AuthMethod, error) {
124142
}
125143

126144
// NewPublicKeysFromFile returns a PublicKeys from a file containing a PEM
127-
// encoded private key.
128-
func NewPublicKeysFromFile(user string, pemFile string) (AuthMethod, error) {
145+
// encoded private key. An encryption password should be given if the pemBytes
146+
// contains a password encrypted PEM block otherwise password should be empty.
147+
func NewPublicKeysFromFile(user, pemFile, password string) (AuthMethod, error) {
129148
bytes, err := ioutil.ReadFile(pemFile)
130149
if err != nil {
131150
return nil, err
132151
}
133152

134-
return NewPublicKeys(user, bytes)
153+
return NewPublicKeys(user, bytes, password)
135154
}
136155

137156
func (a *PublicKeys) Name() string {

plumbing/transport/ssh/auth_method_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,14 @@ func (s *SuiteCommon) TestNewSSHAgentAuth(c *C) {
109109
}
110110

111111
func (*SuiteCommon) TestNewPublicKeys(c *C) {
112-
auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"])
112+
auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"], "")
113+
c.Assert(err, IsNil)
114+
c.Assert(auth, NotNil)
115+
}
116+
117+
func (*SuiteCommon) TestNewPublicKeysWithEncryptedPEM(c *C) {
118+
f := testdata.PEMEncryptedKeys[0]
119+
auth, err := NewPublicKeys("foo", f.PEMBytes, f.EncryptionKey)
113120
c.Assert(err, IsNil)
114121
c.Assert(auth, NotNil)
115122
}
@@ -122,7 +129,7 @@ func (*SuiteCommon) TestNewPublicKeysFromFile(c *C) {
122129
c.Assert(f.Close(), IsNil)
123130
defer os.RemoveAll(f.Name())
124131

125-
auth, err := NewPublicKeysFromFile("foo", f.Name())
132+
auth, err := NewPublicKeysFromFile("foo", f.Name(), "")
126133
c.Assert(err, IsNil)
127134
c.Assert(auth, NotNil)
128135
}

0 commit comments

Comments
 (0)