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

transport: ssh, NewPublicKeys support for encrypted PEM files #355

Merged
merged 1 commit into from
Apr 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions plumbing/transport/ssh/auth_method.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ssh

import (
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
Expand All @@ -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"
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down
11 changes: 9 additions & 2 deletions plumbing/transport/ssh/auth_method_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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)
}