diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..61ead86 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/.travis.yml b/.travis.yml index 0fe3dc1..cedda7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,17 @@ language: go +sudo: false + +go_import_path: github.com/ScaleFT/sshkeys + go: - - 1.6 - - 1.7 - - master + - 1.9.x + - 1.10.x + - 1.11.x + - tip + +before_install: + - go get -u github.com/stretchr/testify/require github.com/dchest/bcrypt_pbkdf golang.org/x/crypto/ed25519 golang.org/x/crypto/ssh -install: - - go get -u github.com/stretchr/testify/require github.com/dchest/bcrypt_pbkdf +script: + - go test -v ./... diff --git a/marshal.go b/marshal.go index 9d37f4e..5fdf209 100644 --- a/marshal.go +++ b/marshal.go @@ -1,6 +1,7 @@ package sshkeys import ( + "crypto/aes" "crypto/cipher" "crypto/dsa" "crypto/ecdsa" @@ -14,9 +15,6 @@ import ( mrand "math/rand" "github.com/dchest/bcrypt_pbkdf" - - "crypto/aes" - "golang.org/x/crypto/ed25519" "golang.org/x/crypto/ssh" ) diff --git a/marshal_test.go b/marshal_test.go index df503e8..7e1299e 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -1,13 +1,13 @@ -package sshkeys +package sshkeys_test import ( "crypto/rand" "testing" - "golang.org/x/crypto/ssh" - + "github.com/ScaleFT/sshkeys" "github.com/ScaleFT/sshkeys/testdata" "github.com/stretchr/testify/require" + "golang.org/x/crypto/ssh" ) func testSigners(t *testing.T, name string, a ssh.Signer, b ssh.Signer) { @@ -15,61 +15,78 @@ func testSigners(t *testing.T, name string, a ssh.Signer, b ssh.Signer) { sign := []byte("hello world") sig, err := a.Sign(rand.Reader, sign) - require.NoError(t, err, "signer failed for "+name) + require.NoError(t, err, "signer failed for %s", name) err = b.PublicKey().Verify(sign, sig) - require.NoError(t, err, "verify failed for "+name) + require.NoError(t, err, "verify failed for %s", name) } -func TestMarshal(t *testing.T) { +func TestMarshalOldFormat(t *testing.T) { password := []byte("gopher") for _, k := range testdata.PEMEncryptedKeys { - pk, err := ParseEncryptedRawPrivateKey(k.PEMBytes, []byte(k.EncryptionKey)) - require.NoError(t, err, "error parsing "+k.Name) - require.NotNil(t, pk, "nil return from parsing "+k.Name) + // ed25519 is only specified in the new format + if k.Name == "ed25519-openssh-encrypted-aes256-cbc" || k.Name == "ed25519-openssh-encrypted-aes256-ctr" { + continue + } + t.Run(k.Name, func(t *testing.T) { + pk, err := sshkeys.ParseEncryptedRawPrivateKey(k.PEMBytes, []byte(k.EncryptionKey)) + require.NoError(t, err, "error parsing %s", k.Name) + require.NotNil(t, pk, "nil return from parsing %s", k.Name) - signer, err := ssh.NewSignerFromKey(pk) - require.NoError(t, err) + signer, err := ssh.NewSignerFromKey(pk) + require.NoError(t, err) - data, err := Marshal(pk, &MarshalOptions{ - Passphrase: password, - Format: FormatClassicPEM, - }) + data, err := sshkeys.Marshal(pk, &sshkeys.MarshalOptions{ + Passphrase: password, + Format: sshkeys.FormatClassicPEM, + }) - // ed25519 is only specified in the new format - if k.Name != "ed25519-openssh-encrypted" { require.NoError(t, err) - require.NotNil(t, data, "nil return from marshaling "+k.Name) + require.NotNil(t, data, "nil return from marshaling %s", k.Name) - pk2, err := ParseEncryptedRawPrivateKey(data, password) - require.NoError(t, err, "error from parsing "+k.Name) - require.NotNil(t, pk2, "nil return from parsing "+k.Name) + pk2, err := sshkeys.ParseEncryptedRawPrivateKey(data, password) + require.NoError(t, err, "error from parsing %s", k.Name) + require.NotNil(t, pk2, "nil return from parsing %s", k.Name) signer2, err := ssh.NewSignerFromKey(pk2) require.NoError(t, err) testSigners(t, k.Name, signer, signer2) - } - - // now use new format - data, err = Marshal(pk, &MarshalOptions{ - Passphrase: password, - Format: FormatOpenSSHv1, }) - if err != nil && err.Error() == "sshkeys: unsupported key type *dsa.PrivateKey" { + } +} + +func TestMarshalNewFormat(t *testing.T) { + password := []byte("gopher") + for _, k := range testdata.PEMEncryptedKeys { + if k.Name == "dsa-encrypted-aes256-cbc" { continue } - require.NoError(t, err) - require.NotNil(t, data, "nil return from marshaling "+k.Name) - // println("input: " + string(data)) - pk3, err := ParseEncryptedRawPrivateKey(data, password) - require.NoError(t, err, "error from parsing "+k.Name) - require.NotNil(t, pk3, "nil return from parsing "+k.Name) + t.Run(k.Name, func(t *testing.T) { + pk, err := sshkeys.ParseEncryptedRawPrivateKey(k.PEMBytes, []byte(k.EncryptionKey)) + require.NoError(t, err, "error parsing %s", k.Name) + require.NotNil(t, pk, "nil return from parsing %s", k.Name) - signer3, err := ssh.NewSignerFromKey(pk3) - require.NoError(t, err) + signer, err := ssh.NewSignerFromKey(pk) + require.NoError(t, err) + + data, err := sshkeys.Marshal(pk, &sshkeys.MarshalOptions{ + Passphrase: password, + Format: sshkeys.FormatOpenSSHv1, + }) + + require.NoError(t, err) + require.NotNil(t, data, "nil return from marshaling %s", k.Name) - testSigners(t, k.Name, signer, signer3) + pk2, err := sshkeys.ParseEncryptedRawPrivateKey(data, password) + require.NoError(t, err, "error from parsing %s", k.Name) + require.NotNil(t, pk2, "nil return from parsing %s", k.Name) + + signer2, err := ssh.NewSignerFromKey(pk2) + require.NoError(t, err) + + testSigners(t, k.Name, signer, signer2) + }) } } diff --git a/parse.go b/parse.go index 117a20b..7ccc2f8 100644 --- a/parse.go +++ b/parse.go @@ -10,6 +10,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" + "crypto/rsa" "crypto/x509" "encoding/pem" "errors" @@ -18,9 +19,6 @@ import ( "strings" "github.com/dchest/bcrypt_pbkdf" - - "crypto/rsa" - "golang.org/x/crypto/ed25519" "golang.org/x/crypto/ssh" ) @@ -112,39 +110,34 @@ func parseOpenSSHPrivateKey(data []byte, passphrase []byte) (interface{}, error) var encrypted bool switch { - // OpenSSH currently only supports bcrypt KDF w/ AES256-CBC mode + // OpenSSH supports bcrypt KDF w/ AES256-CBC or AES256-CTR mode case w.KdfName == "bcrypt" && w.CipherName == "aes256-cbc": - cipherKeylen := keySizeAES256 - cipherIvLen := aes.BlockSize - - var opts struct { - Salt []byte - Rounds uint32 - } - - if err := ssh.Unmarshal([]byte(w.KdfOpts), &opts); err != nil { - return nil, err - } - kdfdata, err := bcrypt_pbkdf.Key(passphrase, opts.Salt, int(opts.Rounds), cipherKeylen+cipherIvLen) + iv, block, err := extractBcryptIvBlock(passphrase, w) if err != nil { return nil, err } - iv := kdfdata[cipherKeylen : cipherIvLen+cipherKeylen] - aeskey := kdfdata[0:cipherKeylen] - block, err := aes.NewCipher(aeskey) + cbc := cipher.NewCBCDecrypter(block, iv) + privateKeyBytes = []byte(w.PrivKeyBlock) + cbc.CryptBlocks(privateKeyBytes, privateKeyBytes) + + encrypted = true + case w.KdfName == "bcrypt" && w.CipherName == "aes256-ctr": + iv, block, err := extractBcryptIvBlock(passphrase, w) if err != nil { return nil, err } - cbc := cipher.NewCBCDecrypter(block, iv) + stream := cipher.NewCTR(block, iv) privateKeyBytes = []byte(w.PrivKeyBlock) - cbc.CryptBlocks(privateKeyBytes, privateKeyBytes) + stream.XORKeyStream(privateKeyBytes, privateKeyBytes) encrypted = true + case w.KdfName == "none" && w.CipherName == "none": privateKeyBytes = []byte(w.PrivKeyBlock) + default: return nil, fmt.Errorf("sshkeys: unknown Cipher/KDF: %s:%s", w.CipherName, w.KdfName) } @@ -221,3 +214,31 @@ func parseOpenSSHPrivateKey(data []byte, passphrase []byte) (interface{}, error) return nil, errors.New("sshkeys: unhandled key type") } } + +func extractBcryptIvBlock(passphrase []byte, w opensshHeader) ([]byte, cipher.Block, error) { + cipherKeylen := keySizeAES256 + cipherIvLen := aes.BlockSize + + var opts struct { + Salt []byte + Rounds uint32 + } + + if err := ssh.Unmarshal([]byte(w.KdfOpts), &opts); err != nil { + return nil, nil, err + } + kdfdata, err := bcrypt_pbkdf.Key(passphrase, opts.Salt, int(opts.Rounds), cipherKeylen+cipherIvLen) + if err != nil { + return nil, nil, err + } + + iv := kdfdata[cipherKeylen : cipherIvLen+cipherKeylen] + aeskey := kdfdata[0:cipherKeylen] + block, err := aes.NewCipher(aeskey) + + if err != nil { + return nil, nil, err + } + + return iv, block, nil +} diff --git a/parse_test.go b/parse_test.go index 42f0f47..c245750 100644 --- a/parse_test.go +++ b/parse_test.go @@ -1,30 +1,35 @@ -package sshkeys +package sshkeys_test import ( "testing" + "github.com/ScaleFT/sshkeys" "github.com/ScaleFT/sshkeys/testdata" "github.com/stretchr/testify/require" ) func TestParse(t *testing.T) { - for name, k := range testdata.PEMBytes { - pk, err := ParseEncryptedPrivateKey(k, nil) - require.NoError(t, err, "error parsing "+name) - require.NotNil(t, pk, "nil return from parsing "+name) + for _, k := range testdata.PEMBytes { + t.Run(k.Name, func(t *testing.T) { + pk, err := sshkeys.ParseEncryptedPrivateKey(k.PEMBytes, nil) + require.NoError(t, err, "error parsing %s", k.Name) + require.NotNil(t, pk, "nil return from parsing %s", k.Name) + }) } } func TestEncryptedParse(t *testing.T) { wrongKey := []byte("hello world") for _, k := range testdata.PEMEncryptedKeys { - pk, err := ParseEncryptedPrivateKey(k.PEMBytes, wrongKey) - require.Error(t, err, "expected error from "+k.Name) - require.Equal(t, err, ErrIncorrectPassword, "expected error from "+k.Name) - require.Nil(t, pk, "non-nil return from parsing "+k.Name) + t.Run(k.Name, func(t *testing.T) { + pk, err := sshkeys.ParseEncryptedPrivateKey(k.PEMBytes, wrongKey) + require.Error(t, err, "expected error from %s", k.Name) + require.Equal(t, err, sshkeys.ErrIncorrectPassword, "expected error from %s", k.Name) + require.Nil(t, pk, "non-nil return from parsing %s", k.Name) - pk, err = ParseEncryptedPrivateKey(k.PEMBytes, []byte(k.EncryptionKey)) - require.NoError(t, err) - require.NotNil(t, pk, "nil return from parsing "+k.Name) + pk, err = sshkeys.ParseEncryptedPrivateKey(k.PEMBytes, []byte(k.EncryptionKey)) + require.NoError(t, err) + require.NotNil(t, pk, "nil return from parsing %s", k.Name) + }) } } diff --git a/testdata/keys.go b/testdata/keys.go index 7dc350d..ab1701c 100644 --- a/testdata/keys.go +++ b/testdata/keys.go @@ -4,8 +4,13 @@ package testdata -var PEMBytes = map[string][]byte{ - "dsa": []byte(`-----BEGIN DSA PRIVATE KEY----- +var PEMBytes = []struct { + Name string + PEMBytes []byte +}{ + { + Name: "dsa", + PEMBytes: []byte(`-----BEGIN DSA PRIVATE KEY----- MIIBuwIBAAKBgQD6PDSEyXiI9jfNs97WuM46MSDCYlOqWw80ajN16AohtBncs1YB lHk//dQOvCYOsYaE+gNix2jtoRjwXhDsc25/IqQbU1ahb7mB8/rsaILRGIbA5WH3 EgFtJmXFovDz3if6F6TzvhFpHgJRmLYVR8cqsezL3hEZOvvs2iH7MorkxwIVAJHD @@ -18,13 +23,19 @@ nOwuxb0Kce+gWI8voWcqC9cyRm09jGzu2Ab3Bhtpg8JJ8L7gS3MRZK4CFEx4UAfY Fmsr0W6fHB9nhS4/UXM8 -----END DSA PRIVATE KEY----- `), - "ecdsa": []byte(`-----BEGIN EC PRIVATE KEY----- + }, + { + Name: "ecdsa", + PEMBytes: []byte(`-----BEGIN EC PRIVATE KEY----- MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49 AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+ 6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA== -----END EC PRIVATE KEY----- `), - "rsa": []byte(`-----BEGIN RSA PRIVATE KEY----- + }, + { + Name: "rsa", + PEMBytes: []byte(`-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQC8A6FGHDiWCSREAXCq6yBfNVr0xCVG2CzvktFNRpue+RXrGs/2 a6ySEJQb3IYquw7HlJgu6fg3WIWhOmHCjfpG0PrL4CRwbqQ2LaPPXhJErWYejcD8 Di00cF3677+G10KMZk9RXbmHtuBFZT98wxg8j+ZsBMqGM1+7yrWUvynswQIDAQAB @@ -40,15 +51,19 @@ z26i6XVMeYXAWZMTloMCQBbpGgEERQpeUknLBqUHhg/wXF6+lFA+vEGnkY+Dwab2 KCXFGd+SQ5GdUcEMe9isUH6DYj/6/yCDoFrXXmpQb+M= -----END RSA PRIVATE KEY----- `), - "ed25519": []byte(`-----BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW -QyNTUxOQAAACA+3f7hS7g5UWwXOGVTrMfhmxyrjqz7Sxxbx7I1j8DvvwAAAJhAFfkOQBX5 -DgAAAAtzc2gtZWQyNTUxOQAAACA+3f7hS7g5UWwXOGVTrMfhmxyrjqz7Sxxbx7I1j8Dvvw -AAAEAaYmXltfW6nhRo3iWGglRB48lYq0z0Q3I3KyrdutEr6j7d/uFLuDlRbBc4ZVOsx+Gb -HKuOrPtLHFvHsjWPwO+/AAAAE2dhcnRvbm1AZ2FydG9ubS14cHMBAg== ------END OPENSSH PRIVATE KEY----- + }, + { + Name: "user", + PEMBytes: []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEILYCAeq8f7V4vSSypRw7pxy8yz3V5W4qg8kSC3zJhqpQoAoGCCqGSM49 +AwEHoUQDQgAEYcO2xNKiRUYOLEHM7VYAp57HNyKbOdYtHD83Z4hzNPVC4tM5mdGD +PLL8IEwvYu2wq+lpXfGQnNMbzYf9gspG0w== +-----END EC PRIVATE KEY----- `), - "rsa-openssh-format": []byte(`-----BEGIN OPENSSH PRIVATE KEY----- + }, + { + Name: "rsa-openssh-format", + PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn NhAAAAAwEAAQAAAIEAwa48yfWFi3uIdqzuf9X7C2Zxfea/Iaaw0zIwHudpF8U92WVIiC5l oEuW1+OaVi3UWfIEjWMV1tHGysrHOwtwc34BPCJqJknUQO/KtDTBTJ4Pryhw1bWPC999Lz @@ -64,12 +79,18 @@ X1ViJuqqcQnJPVzpgSL826EC2xwOECTqoY8uvFpUdD7CtpksIxNVqRIhuNOlz0lqEAAABB ANkaHTTaPojClO0dKJ/Zjs7pWOCGliebBYprQ/Y4r9QLBkC/XaWMS26gFIrjgC7D2Rv+rZ wSD0v0RcmkITP1ZR0AAAAYcHF1ZXJuYUBMdWNreUh5ZHJvLmxvY2FsAQID -----END OPENSSH PRIVATE KEY-----`), - "user": []byte(`-----BEGIN EC PRIVATE KEY----- -MHcCAQEEILYCAeq8f7V4vSSypRw7pxy8yz3V5W4qg8kSC3zJhqpQoAoGCCqGSM49 -AwEHoUQDQgAEYcO2xNKiRUYOLEHM7VYAp57HNyKbOdYtHD83Z4hzNPVC4tM5mdGD -PLL8IEwvYu2wq+lpXfGQnNMbzYf9gspG0w== ------END EC PRIVATE KEY----- + }, + { + Name: "ed25519-openssh-format", + PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACA+3f7hS7g5UWwXOGVTrMfhmxyrjqz7Sxxbx7I1j8DvvwAAAJhAFfkOQBX5 +DgAAAAtzc2gtZWQyNTUxOQAAACA+3f7hS7g5UWwXOGVTrMfhmxyrjqz7Sxxbx7I1j8Dvvw +AAAEAaYmXltfW6nhRo3iWGglRB48lYq0z0Q3I3KyrdutEr6j7d/uFLuDlRbBc4ZVOsx+Gb +HKuOrPtLHFvHsjWPwO+/AAAAE2dhcnRvbm1AZ2FydG9ubS14cHMBAg== +-----END OPENSSH PRIVATE KEY----- `), + }, } var PEMEncryptedKeys = []struct { @@ -77,8 +98,8 @@ var PEMEncryptedKeys = []struct { EncryptionKey string PEMBytes []byte }{ - 0: { - Name: "rsa-encrypted", + { + Name: "rsa-encrypted-aes256-cbc", EncryptionKey: "r54-G0pher_t3st$", PEMBytes: []byte(`-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED @@ -112,9 +133,41 @@ MvMLGkYgQ4Utwnd98mtqa0jr0hK2TcOSFir3AqVvXN3XJj4cVULkrXe4Im1laWgp -----END RSA PRIVATE KEY----- `), }, - - 1: { - Name: "dsa-encrypted", + { + Name: "rsa-encrypted-aes256-ctr", + EncryptionKey: "Password-Test_1234", + PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBb5bVJRJ +OyqhVNPrtqeQR7AAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQDGl3Dz88NH +mWH65wsj94ffJHmXf09tYTIbJKwSADexk8ETQMBG0CjgM5B5Xu9J9JzTG3QkXFkery9tWb +uPCh4y2hz1svJe3gg37YV3Pp98Fmc4rTAKozlJLUdZA8kmURXmGhUe8p6IBT/EF8/ggGqC +vphqBwEzQz8jphAzkDBNFfQCpqEblnwG2Kw/Tegso1BPI9EsN3o5wrURCLN8fjPxxEcmWR +dGcCDuUqAbwnaJRGup0iJPal7YIVSi3uB7VCK8iWeDtsGLpOAnLojJRzGccn6pnFb1Pjes +35VWkhyv1KH5tDjmq0fA+P40ffSV0T1OZ7J0ZyR/BC0yV0gypHCPAAAD0LaQ/bL0QaRXRv +qHn1A+xJIWtkHskTX0APDZ+yyUHye9oaR5LBkvv6x8wPH3j8jb/k6eZ1zfv3g3Xkm5qUOW +dI0Lu1KPrwxh+f3Mco7LCDqHsI+j72uTo3sNJjsxhrRuX31rmKUwP4MWNecwVT9uN6Y+lr +BjFKfZX3RTEfYzgTh2skPVco90x0Zfbj/Q8IWaPpZFdLzogB+kERZrA7HT8SIGmzLOKDCX +KjVP92RkxvleC5T7XcTsXVkVelnfcMQ7sK6tKu6gk3+TUm0FUpOPoPxM1oRfVdyV2Fc3iI +tq17lkexQ3PqAjlSkM9VSz8f0ZEKqkQ2oK7m+TPoasMSkn/Blxs6ZnNiToH9Y9xawva/M5 +JW822Zbhu8vVGBtpB9DVaHAj3JPAc+l2EwSlHPbyWoNfmQcHilonp+mHru2B/fH+vXOYO5 ++T1Fp0gfQMifrDISgcNbTmhKYhnWnnpV6a3JGIe+cUWGzeYn9/GGZAJOHyAyA7Uy70e/2M +9ps4AY2rsS4Fu21BHuskbs9X1ujszIk0prXEN5NAyvKDsTT9chQRjo/VbwWpeAN9adF1Gs +yHnOkVT4Vc1reCLi0hNGcqUVH+LAQwTcaEnNaY1mAdgSBY+/8CnSDkhffixzT5ob5HbfLG +M2Hax9/pDX9hlLoh+eHz5pLoKzW+YDo1/NypS7goAsHPkiMTpn+oM1bqtjxDLeNmh3gCdW +UlJYt8rUT4G0t8JFIhTlVUNULaRYVIypGPgKdkVqUYo3lRW959cxgAC92XepjBISRACQCT +jYBWu2RHzbgePTwQLui0udXXFrhL7Tjw+epudZV3CDenBpkaDIGHOb3vOHN/q/lhgyuaTE +KN02DGQ1ThBdpAbUJksMj3S8X4aYtZK5Q71o7szjuamf+UseausaLs47BaNKqLugfDNFFl +oK2A5VCwJrt+o1wdet86JSnwr5lcJlSF4X9N8Ju6W0qJ/RWoE6znRpuCsVq90i0cgFaVIA +OE8CeHdjpXHijVqW/cFbH2IDyUYzSdr5Ke1fow0M61n22D9lm16mXBs5OmkZ5yrb7vJCB7 +U2VYIrZIsGQh2VMC/HOt3Ms3HE08jI97MDX/4LlmQEzZwDLYArK7JVVpTCvjdTiY2XrSd6 +ZRE5Mw1J5QYHHdw18Ia+tvKVlcQL+RwbTDYzBQrkmHIJ7xYAO5QAtRMZnwSGRyq+f8uhEU +nGz9lcHnccxsF6BxuT0XHe1gcUnU71Uffm+D9NLf9xGxEgWkoJK4+/K5DxRYXxYj2iWSMg +xQGBlJ2l92yvGabNDbfgqgsvVsTWg= +-----END OPENSSH PRIVATE KEY----- +`), + }, + { + Name: "dsa-encrypted-aes256-cbc", EncryptionKey: "qG0pher-dsa_t3st$", PEMBytes: []byte(`-----BEGIN DSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED @@ -133,8 +186,8 @@ IiHM7GBn+0nJoKTXsOGMIBe3ulKlKVxLjEuk9yivh/8= -----END DSA PRIVATE KEY----- `), }, - 2: { - Name: "rsa-openssh-encrypted", + { + Name: "rsa-openssh-encrypted-aes256-cbc", EncryptionKey: "12345", PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABAr17kxS0 @@ -155,8 +208,8 @@ uxJ4W16r7KrATcy4l5F45EyinbHR020= -----END OPENSSH PRIVATE KEY----- `), }, - 3: { - Name: "ed25519-openssh-encrypted", + { + Name: "ed25519-openssh-encrypted-aes256-cbc", EncryptionKey: "quohwan1Ae", PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABBkYM/1no @@ -165,6 +218,62 @@ PsElCyrgFXmGikfg9chCGrY6MW2fAAAAkF2mStmLKKdTDi67NQeZiWv1FaCd9NyKyUYY9s KkgIWxvVVzi8SxJmR2znR4PU26HpX3QUDdOf6ET3BATO9VVWkbBUUqt0VnwB1CI498OYuQ uFv91uY8u4KfYYFN7qZw4sjjPa5m+u1cIGwqVDoNDXR7OddStXwDCcn/9Wq+t2/LoavtsC MgjCX2e9GImZ7kbA== +-----END OPENSSH PRIVATE KEY----- + `), + }, + { + Name: "ed25519-openssh-encrypted-aes256-ctr", + EncryptionKey: "1234", + PEMBytes: []byte(`-----BEGIN OPENSSH PRIVATE KEY----- +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 +s4LetqIGnhtXJnHsb2en0cZaM= -----END OPENSSH PRIVATE KEY----- `), - }} + }, +}