Merge pull request #1 from stephen-fox/parse-user-supplied-keypair

Parse user supplied keypair
This commit is contained in:
stephen-fox 2019-02-06 15:07:04 -05:00 committed by GitHub
commit b7719650ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 495 additions and 14 deletions

View File

@ -36,7 +36,19 @@ func (s *StepSshKeyPair) Run(_ context.Context, state multistep.StateBag) multis
return multistep.ActionHalt return multistep.ActionHalt
} }
kp, err := ssh.NewKeyPairBuilder().
SetPrivateKey(privateKeyBytes).
SetName(fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())).
Build()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
s.Comm.SSHPrivateKey = privateKeyBytes s.Comm.SSHPrivateKey = privateKeyBytes
s.Comm.SSHKeyPairName = kp.Name()
s.Comm.SSHTemporaryKeyPairName = kp.Name()
s.Comm.SSHPublicKey = kp.PublicKeyAuthorizedKeysLine(ssh.UnixNewLine)
return multistep.ActionContinue return multistep.ActionContinue
} }

View File

@ -0,0 +1,28 @@
package ssh
type defaultKeyPair struct {
}
func (o defaultKeyPair) Type() KeyPairType {
return Default
}
func (o defaultKeyPair) Bits() int {
return 0
}
func (o defaultKeyPair) Name() string {
return ""
}
func (o defaultKeyPair) Description() string {
return ""
}
func (o defaultKeyPair) PrivateKeyPemBlock() []byte {
return []byte{}
}
func (o defaultKeyPair) PublicKeyAuthorizedKeysLine(nl NewLineOption) []byte {
return []byte{}
}

View File

@ -0,0 +1,38 @@
package ssh
import (
"crypto/dsa"
gossh "golang.org/x/crypto/ssh"
)
type dsaKeyPair struct {
privateKey *dsa.PrivateKey
publicKey gossh.PublicKey
name string
privatePemBlock []byte
}
func (o dsaKeyPair) Type() KeyPairType {
return Dsa
}
func (o dsaKeyPair) Bits() int {
return 1024
}
func (o dsaKeyPair) Name() string {
return o.name
}
func (o dsaKeyPair) Description() string {
return description(o)
}
func (o dsaKeyPair) PrivateKeyPemBlock() []byte {
return o.privatePemBlock
}
func (o dsaKeyPair) PublicKeyAuthorizedKeysLine(nl NewLineOption) []byte {
return publicKeyAuthorizedKeysLine(o.publicKey, o.name, nl)
}

View File

@ -0,0 +1,38 @@
package ssh
import (
"golang.org/x/crypto/ed25519"
gossh "golang.org/x/crypto/ssh"
)
type ed25519KeyPair struct {
privateKey *ed25519.PrivateKey
publicKey gossh.PublicKey
name string
privatePemBlock []byte
}
func (o ed25519KeyPair) Type() KeyPairType {
return Ed25519
}
func (o ed25519KeyPair) Bits() int {
return 256
}
func (o ed25519KeyPair) Name() string {
return o.name
}
func (o ed25519KeyPair) Description() string {
return description(o)
}
func (o ed25519KeyPair) PrivateKeyPemBlock() []byte {
return o.privatePemBlock
}
func (o ed25519KeyPair) PublicKeyAuthorizedKeysLine(nl NewLineOption) []byte {
return publicKeyAuthorizedKeysLine(o.publicKey, o.name, nl)
}

View File

@ -2,6 +2,7 @@ package ssh
import ( import (
"bytes" "bytes"
"crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
@ -9,9 +10,11 @@ import (
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt"
"strconv" "strconv"
"strings" "strings"
"golang.org/x/crypto/ed25519"
gossh "golang.org/x/crypto/ssh" gossh "golang.org/x/crypto/ssh"
) )
@ -19,11 +22,12 @@ const (
// That's a lot of bits. // That's a lot of bits.
defaultRsaBits = 4096 defaultRsaBits = 4096
// Rsa is a SSH key pair of RSA type. // Markers for various SSH key pair types.
Rsa KeyPairType = "RSA" Default KeyPairType = ""
Rsa KeyPairType = "RSA"
// Ecdsa is a SSH key pair of ECDSA type. Ecdsa KeyPairType = "ECDSA"
Ecdsa KeyPairType = "ECDSA" Dsa KeyPairType = "DSA"
Ed25519 KeyPairType = "ED25519"
) )
// KeyPairType represents different types of SSH key pairs. // KeyPairType represents different types of SSH key pairs.
@ -58,6 +62,9 @@ func (o NewLineOption) Bytes() []byte {
} }
// KeyPairBuilder builds SSH key pairs. // KeyPairBuilder builds SSH key pairs.
// It can generate new keys of type RSA and ECDSA.
// It can parse user supplied keys of type DSA, RSA, ECDSA,
// and ED25519.
type KeyPairBuilder interface { type KeyPairBuilder interface {
// SetType sets the key pair type. // SetType sets the key pair type.
SetType(KeyPairType) KeyPairBuilder SetType(KeyPairType) KeyPairBuilder
@ -65,10 +72,15 @@ type KeyPairBuilder interface {
// SetBits sets the key pair's bits of entropy. // SetBits sets the key pair's bits of entropy.
SetBits(int) KeyPairBuilder SetBits(int) KeyPairBuilder
// SetName sets the name of the key pair. This is primarily used // SetName sets the name of the key pair. This is primarily
// to identify the public key in the authorized_keys file. // used to identify the public key in the authorized_keys file.
SetName(string) KeyPairBuilder SetName(string) KeyPairBuilder
// SetPrivateKey takes an existing private key in PEM format.
// It overrides key generation details specified by SetType()
// and SetBits().
SetPrivateKey([]byte) KeyPairBuilder
// Build returns a SSH key pair. // Build returns a SSH key pair.
// //
// The following defaults are used if not specified: // The following defaults are used if not specified:
@ -89,6 +101,10 @@ type defaultKeyPairBuilder struct {
// name is the resulting key pair's name. // name is the resulting key pair's name.
name string name string
// privatePemBytes is the supplied key data when the builder
// is working from a preallocated key.
privatePemBytes []byte
} }
func (o *defaultKeyPairBuilder) SetType(kind KeyPairType) KeyPairBuilder { func (o *defaultKeyPairBuilder) SetType(kind KeyPairType) KeyPairBuilder {
@ -106,15 +122,82 @@ func (o *defaultKeyPairBuilder) SetName(name string) KeyPairBuilder {
return o return o
} }
func (o *defaultKeyPairBuilder) SetPrivateKey(privateBytes []byte) KeyPairBuilder {
o.privatePemBytes = privateBytes
return o
}
func (o *defaultKeyPairBuilder) Build() (KeyPair, error) { func (o *defaultKeyPairBuilder) Build() (KeyPair, error) {
if o.privatePemBytes != nil {
return o.preallocatedKeyPair()
}
switch o.kind { switch o.kind {
case Rsa: case Rsa:
return o.newRsaKeyPair() return o.newRsaKeyPair()
case Ecdsa: case Ecdsa, Default:
// Default case. return o.newEcdsaKeyPair()
} }
return o.newEcdsaKeyPair() return defaultKeyPair{}, fmt.Errorf("Unsupported keypair type: %s", o.kind.String())
}
// preallocatedKeyPair returns an SSH key pair based on user
// supplied PEM data.
func (o *defaultKeyPairBuilder) preallocatedKeyPair() (KeyPair, error) {
privateKey, err := gossh.ParseRawPrivateKey(o.privatePemBytes)
if err != nil {
return nil, err
}
switch pk := privateKey.(type) {
case *rsa.PrivateKey:
publicKey, err := gossh.NewPublicKey(&pk.PublicKey)
if err != nil {
return nil, err
}
return &rsaKeyPair{
privateKey: pk,
publicKey: publicKey,
name: o.name,
privatePemBlock: o.privatePemBytes,
}, nil
case *ecdsa.PrivateKey:
publicKey, err := gossh.NewPublicKey(&pk.PublicKey)
if err != nil {
return nil, err
}
return &ecdsaKeyPair{
privateKey: pk,
publicKey: publicKey,
name: o.name,
privatePemBlock: o.privatePemBytes,
}, nil
case *dsa.PrivateKey:
publicKey, err := gossh.NewPublicKey(&pk.PublicKey)
if err != nil {
return nil, err
}
return &dsaKeyPair{
privateKey: pk,
publicKey: publicKey,
name: o.name,
privatePemBlock: o.privatePemBytes,
}, nil
case *ed25519.PrivateKey:
publicKey, err := gossh.NewPublicKey(pk.Public())
if err != nil {
return nil, err
}
return &ed25519KeyPair{
privateKey: pk,
publicKey: publicKey,
name: o.name,
privatePemBlock: o.privatePemBytes,
}, nil
}
return &defaultKeyPair{}, fmt.Errorf("Unknown ssh key pair type")
} }
// newEcdsaKeyPair returns a new ECDSA SSH key pair. // newEcdsaKeyPair returns a new ECDSA SSH key pair.

View File

@ -20,6 +20,137 @@ type expected struct {
data []byte data []byte
} }
const (
pemRsa1024 = `-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDJEMFPpTBiWNDb3qEIPTSeEnIP8FZdBpG8njOrclcMoQQNhzZ+
4uz37tqtHMp36Z7LB4/+85NN6epNXO+ekyZIHswiyBcJC2sT3KuH7nG1BESOooPY
DfeCSM+CJT9GDIhy9nUXSsJjrceEyh/B5DjEtIbS0XfcRelrNTJodCmPJwIDAQAB
AoGAK66GMOV0c4lUJtBhL8cMTWM4gJn4SVGKC+5az16R5t58YOwFPN/UF7E+tOlS
W2bX5sgH0p3cXMr66j/Mlyjk4deLg7trDavulIP93MyVO2SUJ0cstQ0ZmRz2oGwx
Gow+hD75Cet7uvepdmG4DKHJe8D/I72rtP1WKuZyd0vP6WECQQDua6wWlyEdIimx
XoGWUvmywACWPnQmBnyHG7x5hxMjijQoQZu60zRxSU9I5q08BerTsvbTc+xLnDVv
mFzlcjT/AkEA1+P7lcvViZeNKoDB1Qt+VV+pkcqL5aoRwdnLA51SyFJ9tXkxeZwA
LOof3xtoRGhCld7ixi3kF5aZsafAJOZd2QJAH8rFyMFgTgU3MAqdFxF7cGV/7ojn
bgahZlbBfCcR20Rbjh6piHEPZifTZbI02XMkjBQqK6oikTaEPZxAjuv6uwJANczu
yWm+kUdfOpRTuY/fr87jJx3etyEmw7ROz1vJYXqNMUg+eBvUP10pDCR8W2/QCCE/
Sjvtd6NkMc2oKInwIQJAFZ1xJte0EaQsXaCIoZwHrQJbK1dd5l1xTAzz51voAcKH
2K23xgx4I+/eam2enjFa7wXLZFoW0xg/51xsaIjnrA==
-----END RSA PRIVATE KEY-----
`
pemRsa2048 = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA/ZPazeRmBapF01gzHXtJGpu0S936xHY+pOrIyIk6lEE06paf
q5gh6BCuiN/60Keed5Nz+Es4dPGc73mql9pd7N0HOoEc1IQjZzJVqWOy3E55oWbz
rXr1qbmMjw8bGHalZsVBov1UhyB6f2bKi88fGkThJi9HZ+Dc3Jr87eW+whS4D0bI
JJe5dkY0VhDqB0YVEk299TxlAiDkeXD1EcMZrD/yHsusapwlXL2WHWmCgbPpbeYW
YJhD1bScChYmf41iiInBwFymG7kz4bPsup7wCBXpcLJplY1iuXdtVVujNLDbJwlb
Xi2oBm3WizPjYcUthvMlqOieuy6Z4KzyJd7EnQIDAQABAoIBAByZ8LQIbvl0myub
ZyiMH1LA/TURdJd0PtybHsp/r/vI3w8WrivMnQZv2z/VA5VFUrpqB0qaMWP/XJQm
RPebybxNseMHbRkLTnL1WnQgqqvurglmc1W96LecFh6MtaGswDs3RI/9wur63tY/
4dijI/7yhfKoooU097RqRt0ObNW3BxGwNKUraMLKEZjtohv1cZBeRqzGZuui351E
YsG1jt23/3OP3Acfd1xpzoi+daadxl9JTr02kE7lMjfq32quhTdzuNZP84sQsaV+
RXLNEoiSufjzy3nHTEpG6QaEWQc4gszCIBVRabxr7LtIOqJn2KmXxtOyFE52AJJj
ls3ifAECgYEA/9K+5oHdZBizWWUvNzQWXrdHUtVavCwjj+0v+yRFZSoAhKtVmLYl
8R4NeG6NCIOoJsqmGVpgtCyPndR4PQ6yr4Jt1FJorjsNw21eYrjOVG+y9Z0DkCwJ
uCRVUeqB42jLu7v9r1V3OBQdKLN6VxO4np05KEZyv1LOGGt0XC8NCykCgYEA/cC2
NR7Y4Z5OjCc2GHMQNrVZ2HTDDir71RjcIIEmsIQ5/AMAELFLCSqzD73tJ87T5jPi
aWeOpIcnK78jMvIIsbV0BXaDsjtlvCdQui2AoX63FuK4q4E+vwe5Q/TqY2nDh2io
mGHfeXECyUx4gxIede2XEO9zYQ0lP8gxnjmLkFUCgYBO8LolqQcm/xRAzp9eOn14
prekkN+Z10j1/avjpFKhn+9fAPu9zt8wYySm9/4fFXlK1xegFSpoDqQWgNzFgoaS
7/1yGifhM6nQlywb7IkGtx0S+2uBDoXFQ7jsOR/xi4HqoVzrwMS0EkjZKWDkA9rh
XwSnL+3yqduc33OdiotM2QKBgCgNCrVHsSOrQOqOJdOmFaEM7qljhIXv8t+nlNbs
i5bAyAYm0xPPZ/CCdNC/QXdPBdMHzWylk7YUPvKAsKWR3h1ubmmOUysGhQA1lGBO
XkcfIPbTwiIPvD+akHtRZM1cHCh7NGEY0ZTxaWcsUrkdWwFyBq39nVBsKrzudCZt
HsIhAoGBAMv3erZQawzIgX9nOUHB2UJl8pS+LZSNMlYvFoMHKI2hLq0g/VxUlnsh
Jzw9+fTLMVFdY+F3ydO6qQFd8wlfov7deyscdoSj8R5gjGKJsarBs+YVdFde2oLG
gkFsXmbmc2boyqGg51CbAX34VJOhGQKhWgKCWqDGmoYXafmyiZc+
-----END RSA PRIVATE KEY-----
`
pemOpenSshRsa1024 = `-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAIEAzzknaHV741775aJOPacDpd2SiDpIDYmm7/w2sgY8lrinSakfLIVk
1qn0IBRLNOzMxoF/pvIgGQXS51xvE1vB3QK8L+8vJwH06DuOXPP1WgVoDTU03gGvBJ7MNF
5HcQYvBiIaU5XxG8l0OZO88B9RFhPP9r0XrYxAlSjuk9KKlEcAAAIYLQ46zy0OOs8AAAAH
c3NoLXJzYQAAAIEAzzknaHV741775aJOPacDpd2SiDpIDYmm7/w2sgY8lrinSakfLIVk1q
n0IBRLNOzMxoF/pvIgGQXS51xvE1vB3QK8L+8vJwH06DuOXPP1WgVoDTU03gGvBJ7MNF5H
cQYvBiIaU5XxG8l0OZO88B9RFhPP9r0XrYxAlSjuk9KKlEcAAAADAQABAAAAgQDJ9Jq6jF
08P/LhXug/38iHW0UW7S4Ru4jttHGd2MQt5DJtcJzIKA0ZxLL+nKibIPmFsOm2y5yKpolg
IE7EoBVzTeg0LedbRayc0Kc5tY7PEz0Shi9ABIMYbNo2L2pNmsq9ns0xA8ur3OugfKHsH8
XjJ1rdHsyLjoMx2ADfLY0xkQAAAEAvyrgW4jswENdErbF0rOdP+Y73B/8rxBaY/QBE2qtG
oUp7bpOtUAH2Ip7RjXOX4xTAt4n2QeHBSfX7gfXRjmY6AAAAQQDmYlgSWtTYLV9VZSScLU
OG+GkhQxYqkKN/N9LSpTP4Pwh81KpMp40yvIlufmKLgGihWVxUDzRap3aoR7PqIvHPAAAA
QQDmQ47VwclxiVn5tVAht/Lk2ZVa7rSjeFlXAkAWZkUAiHboaH8IfW9W4gYV7o2BqJO11L
0vi+vCq+le45F416wJAAAAImNocmlzQHBvZXRhc3Rlci5jb3JwLm11dHVhbGluay5uZXQ=
-----END OPENSSH PRIVATE KEY-----
`
pemOpenSshRsa2048 = `-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAxWfWNu0i8sbmwPqTUfKSeXOSt/fLMuqucn9KYU7rJ+83trznRhAn
AHQzKgcSU8PBgkax+PDEUexYUB9gZApNI6K/2twVDYh3Hgwx7EjXf05rji7bQk6TFyKEp4
n348CWAdK8iFmNUutSpJLy7GciyMPLu3BK+EsXpsnuPpIm184hEFOiADZyHTGeUgvsKOAc
G7u5hBS3kty8LRZmL+pihbktFwGC4D5bapCcTaF2++zkUy4JKcVE5/2JfK1Ya6D0ATczjz
1b6+r7j2RUg1mXfK6AwMHEcamzhgeuM9RdrPtMdhZI09LCJzjmXc9pzlGu1HCZzh3rJ3hd
8PVmlAd3VQAAA+A9hesQPYXrEAAAAAdzc2gtcnNhAAABAQDFZ9Y27SLyxubA+pNR8pJ5c5
K398sy6q5yf0phTusn7ze2vOdGECcAdDMqBxJTw8GCRrH48MRR7FhQH2BkCk0jor/a3BUN
iHceDDHsSNd/TmuOLttCTpMXIoSniffjwJYB0ryIWY1S61KkkvLsZyLIw8u7cEr4Sxemye
4+kibXziEQU6IANnIdMZ5SC+wo4Bwbu7mEFLeS3LwtFmYv6mKFuS0XAYLgPltqkJxNoXb7
7ORTLgkpxUTn/Yl8rVhroPQBNzOPPVvr6vuPZFSDWZd8roDAwcRxqbOGB64z1F2s+0x2Fk
jT0sInOOZdz2nOUa7UcJnOHesneF3w9WaUB3dVAAAAAwEAAQAAAQEAvA8Z8iWjX6nA9yM/
6ZevluhVY9E60XzlR8qgL2ehet/YMcxwfzywCyyn+WfXO9mHpfZ3YfLs9Ca2U04w4900c7
h+EaAMpmHVKNjxTmpucadhq4hT9S0pz6ZgvcMgVuaHgaEjXroBencYuhQMPM5cQurUUfK+
WSAgnhJNV2qgeoEGgfDZoL1HkItckEZwIzmx4lfMVAuaeqVq5tJNcdv5ukNHpnIYl6fgDp
WGUn/9F8sSHO7P7kGl67IZIsAz+1wW+6pFaVgxbZJ3baPiURtRp+nRSaKLYZSMph6MAiTu
YC8EEVqi3X4m/ZHy+BkphfzR24ouwpt1Vv9QOAPzXXsPwQAAAIEAvmA+yiBdzsJplCifTA
KljE+KpSuvLPRPTb7MGsBO0LIvxrkXOMCXZF4I2VP1zSUH+SDPPc6JeR1Q8liMqPC3Md6c
CIkHfVFBAZL709d0ZtTiir1BipG/l5vIpBnepNX/bWIszIOMzPF2at0WF1lFe6THWujuE8
Xjp2AJSFZlUjAAAACBAOMxr6FN38VwRC1nrDcZyo4TjWVhAdk4p3AkdNJhFSiKS/x5/yo2
K1majzcKbrR8+fEPTVWGszAg+AXQdsOq17q+DMenfrBckQ9ZHr3upSZAaGN+keNwge/Kaj
yOvYiKdYFXmAulQZCPQsDNp7e7Z1dTqxi5IlhUgDPzzO0vRGjNAAAAgQDeb0Ulv7fkYAav
tZ+D0LohGjlGFwTeLdwErcVnq9wGyupdeNhlTXZXxRvF+yEt4FCV9UEUnRX75CAnpk2hT2
D5uYMyixAEfSeIo59Ln27MmAy0alR3UnT7JnLEZRh4dnvFbSSMJ1rHxf8Eg6YFJmpH65fX
exrJE+p69wgRVndoqQAAACJjaHJpc0Bwb2V0YXN0ZXIuY29ycC5tdXR1YWxpbmsubmV0AQ
IDBAUGBw==
-----END OPENSSH PRIVATE KEY-----
`
pemDsa = `-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQDH/T+IkpbdA9nUM7O4MMRoeS0bn7iXWs63Amo2fsIyJPxDvjjF
5HZBH5Rq045TFCCWHjymwiYof+wvwUMZIUH++ABTrKzes/r5qG5jXp42pFWf6nTI
zHwttdjvNiXr+AgreXOrJKhjv6Ga3hq8MNcXMa9xFsIB83EZNMBPxbj0nwIVAJQW
1eR4Uf8/8haQb4HkTsoH+R5/AoGBAK9FV5LIZxY1TeNsD5eXoqpTqCy1WROMggSG
VZ4yN0rrKCtLd8am61m/L8VCMUWiO3IJQdq3yWBTEBbsShL/toau9beUdTl6rdB8
wcEcNgtZnhypQR58HlmgUFWC45rW37hW4AUJuMDgLxgqSVuoF1pDcHrHSi/fZwgp
7t0MKH2SAoGAJfUcLrXg5ZR8jbpZs4L/ubibUn+y35y+33aos07auMW1MesuNTcZ
Ch42nbH2wKnbjk8eDxHdHLHzzOLGgYVMpUuBeuc7G5Q94rM/Z0I8HGQ6mvIkuFyp
58Unu5yu33GsNUgGEHmriiMGezXNXGNH/72PmTXuyxEMSrad23c6NZoCFAtIqbal
4tGCfnnmWU514A7ZzEKj
-----END DSA PRIVATE KEY-----
`
pemEcdsa384 = `-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAjuEIlmFyhGjFtJoAwD420FuPAjIknN3YwDZL4cfMFpB4YAK+7QVLs
coAJ/ADuT7OgBwYFK4EEACKhZANiAASeXKyBr2prr4f4aOsM4dtVikYOUIL3yYnb
GFOy7yHmauCnkIB48paXpvRE5m53Q8zgu7vkz/z9tcMBcC0GzpY3Sef37fmgTUuZ
AJuJp36DMBdQel+j51TcQ79sizxCayg=
-----END EC PRIVATE KEY-----
`
pemEcdsa521 = `-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBVCiwcf/did2vCIu3aMe7OeTD35PULm0hqmfkAK9OKIosi/DjOFfA
8h99rVNPaf+Cx/JNmEzR4bZNnYDyilSRCr+gBwYFK4EEACOhgYkDgYYABABHBMLP
XbQoRF31ZGIeUj9jt9GqKES1dLBtGDEQSiiZFouL4tEIW7NfIZDpOIkA0khNcO8N
xH6eylg0XOgcr01GRwCjY5VOapOahtn63SpajPGeKk+46F2dULIwrov9tWQuYNa3
P50N8j3rx6fAdgyDENOcCJlfNdNcySvkH4bgL1xcsw==
-----END EC PRIVATE KEY-----
`
pemOpenSshEd25519 = `-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAUftPhZQN17kAlThiiWJEgJvddm/pUhHvgrHUtpuYFOQAAAKjN+UhDzflI
QwAAAAtzc2gtZWQyNTUxOQAAACAUftPhZQN17kAlThiiWJEgJvddm/pUhHvgrHUtpuYFOQ
AAAEANXlEZdNU03RMmj77O2ojWh06Hbj8/qQ++H5wkt688NBR+0+FlA3XuQCVOGKJYkSAm
912b+lSEe+CsdS2m5gU5AAAAImNocmlzQHBvZXRhc3Rlci5jb3JwLm11dHVhbGluay5uZX
QBAgM=
-----END OPENSSH PRIVATE KEY-----
`
)
func (o expected) matches(kp KeyPair) error { func (o expected) matches(kp KeyPair) error {
if o.kind.String() == "" { if o.kind.String() == "" {
return errors.New("expected kind's value cannot be empty") return errors.New("expected kind's value cannot be empty")
@ -86,18 +217,18 @@ func (o expected) verifyPublicKeyAuthorizedKeysFormat(kp KeyPair) error {
switch nl { switch nl {
case NoNewLine: case NoNewLine:
if publicKeyAk[len(publicKeyAk) - 1] == '\n' { if publicKeyAk[len(publicKeyAk)-1] == '\n' {
return errors.New("public key in authorized keys format has trailing new line when none was specified") return errors.New("public key in authorized keys format has trailing new line when none was specified")
} }
case UnixNewLine: case UnixNewLine:
if publicKeyAk[len(publicKeyAk) - 1] != '\n' { if publicKeyAk[len(publicKeyAk)-1] != '\n' {
return errors.New("public key in authorized keys format does not have unix new line when unix was specified") return errors.New("public key in authorized keys format does not have unix new line when unix was specified")
} }
if string(publicKeyAk[len(publicKeyAk) - 2:]) == WindowsNewLine.String() { if string(publicKeyAk[len(publicKeyAk)-2:]) == WindowsNewLine.String() {
return errors.New("public key in authorized keys format has windows new line when unix was specified") return errors.New("public key in authorized keys format has windows new line when unix was specified")
} }
case WindowsNewLine: case WindowsNewLine:
if string(publicKeyAk[len(publicKeyAk) - 2:]) != WindowsNewLine.String() { if string(publicKeyAk[len(publicKeyAk)-2:]) != WindowsNewLine.String() {
return errors.New("public key in authorized keys format does not have windows new line when windows was specified") return errors.New("public key in authorized keys format does not have windows new line when windows was specified")
} }
} }
@ -267,3 +398,154 @@ func TestDefaultKeyPairBuilder_Build_NamedRsa(t *testing.T) {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
} }
func TestDefaultKeyPairBuilder_SetPrivateKey(t *testing.T) {
name := uuid.TimeOrderedUUID()
pemData := make(map[string]expected)
pemData[pemRsa1024] = expected{
bits: 1024,
kind: Rsa,
name: name,
desc: "1024 bit RSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemRsa2048] = expected{
bits: 2048,
kind: Rsa,
name: name,
desc: "2048 bit RSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemOpenSshRsa1024] = expected{
bits: 1024,
kind: Rsa,
name: name,
desc: "1024 bit RSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemOpenSshRsa2048] = expected{
bits: 2048,
kind: Rsa,
name: name,
desc: "2048 bit RSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemDsa] = expected{
bits: 1024,
kind: Dsa,
name: name,
desc: "1024 bit DSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemEcdsa384] = expected{
bits: 384,
kind: Ecdsa,
name: name,
desc: "384 bit ECDSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemEcdsa521] = expected{
bits: 521,
kind: Ecdsa,
name: name,
desc: "521 bit ECDSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemOpenSshEd25519] = expected{
bits: 256,
kind: Ed25519,
name: name,
desc: "256 bit ED25519 named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
for s, l := range pemData {
kp, err := NewKeyPairBuilder().SetPrivateKey([]byte(s)).SetName(name).Build()
if err != nil {
t.Fatal(err)
}
err = l.matches(kp)
if err != nil {
t.Fatal(err)
}
}
}
func TestDefaultKeyPairBuilder_SetPrivateKey_Override(t *testing.T) {
name := uuid.TimeOrderedUUID()
pemData := make(map[string]expected)
pemData[pemRsa1024] = expected{
bits: 1024,
kind: Rsa,
name: name,
desc: "1024 bit RSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemRsa2048] = expected{
bits: 2048,
kind: Rsa,
name: name,
desc: "2048 bit RSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemOpenSshRsa1024] = expected{
bits: 1024,
kind: Rsa,
name: name,
desc: "1024 bit RSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemOpenSshRsa2048] = expected{
bits: 2048,
kind: Rsa,
name: name,
desc: "2048 bit RSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemDsa] = expected{
bits: 1024,
kind: Dsa,
name: name,
desc: "1024 bit DSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemEcdsa384] = expected{
bits: 384,
kind: Ecdsa,
name: name,
desc: "384 bit ECDSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemEcdsa521] = expected{
bits: 521,
kind: Ecdsa,
name: name,
desc: "521 bit ECDSA named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
pemData[pemOpenSshEd25519] = expected{
bits: 256,
kind: Ed25519,
name: name,
desc: "256 bit ED25519 named " + name,
data: []byte(uuid.TimeOrderedUUID()),
}
supportedKeyTypes := []KeyPairType{Rsa, Dsa}
for _, keyType := range supportedKeyTypes {
for pemString, expectedResult := range pemData {
kp, err := NewKeyPairBuilder().
SetPrivateKey([]byte(pemString)).
SetName(name).
SetType(keyType).
Build()
if err != nil {
t.Fatal(err)
}
err = expectedResult.matches(kp)
if err != nil {
t.Fatal(err)
}
}
}
}