├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── lib ├── key_read.go ├── serialize.go ├── vault.go ├── vault_read.go ├── vault_test.go └── vault_write.go ├── main.go └── test ├── authorized_keys ├── base64.txt ├── t1_id_rsa ├── t1_id_rsa.pub ├── t2_id_rsa ├── t2_id_rsa.pub └── t3_id_rsa /.gitignore: -------------------------------------------------------------------------------- 1 | ssh-crypt* 2 | vendor -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | deploy: 3 | provider: releases 4 | api_key: 5 | secure: NqOIAqJFfepiEZCqzU3WlOuE1jj0KA561DK74PKQ6UnJRXX4WrikkaB2zussWqdQ3XqHR9GmBDYXcY8qxmp2SxaoGLuXINgjb8dOx15XZbwyToqerzdUgJr59kgtBLMP8P1yBO7m95ZKX5t0LVJt/DTJcHEK4AyCcISY+mFxCzWATY0LjFVUjThGXJRElPl8Y/0dO3Dx2NgvfJpvA6GqOXPG/AYmYWFQgXQ0mgek7vPH339KQIVEwV/Up/ywNV8HErzmyLfGsZmLoze+WCByEYJn7o3M4lSKYPNgj2k71jlY/VPPp9ZHU1Lry4Q+ObdrfdEWZH43R6ZfTBcFOw2NZ8KZDzeoRcafLFt5rBgATTi5m2lHnaHhWIqm5NfdE2cEZBitfuIuQoKox3YIkZj63nl6n69Tzt5mmsXm+8IXQ9rM26RqxJ+NqAjmUAirm1RClFGrnPlcd9EoxWA/2IrDtECVc3gQ3ndNEBFbP7eSVdXXsXkqToSud18QMKH0+5XCr7HTDUKYslcxAe5QDo7Ngq3fa8mtSOW/Hzk1Rcp6gNoJpSuq47hn05MIiLIfniCjf+1br2ncp8hNeD+SJEuv2HzN3ZBSLJQ8EuUY9iBVDFE/LE4lnMO6ScTSZtGqSEkrh9r8s4vzSMn592ib0MZEh3NvdQ7FbJiJ7ZXM67lyYSg= 6 | file: 7 | - ssh-crypt 8 | - ssh-crypt_darwin 9 | - ssh-crypt.exe 10 | skip_cleanup: true 11 | on: 12 | tags: true 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Maciej Filipiak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CURRENT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) 2 | VERSION := $(shell git describe --tag --always --long) 3 | 4 | build: ssh-crypt ssh-crypt.exe ssh-crypt_darwin 5 | 6 | ssh-crypt: $(wildcard *.go) $(wildcard **/*.go) 7 | go get ./... 8 | go get github.com/stretchr/testify/assert 9 | go get github.com/mattn/goveralls 10 | go test -v github.com/suside/ssh-crypt/lib -coverprofile=main.coverprofile 11 | go build -o ssh-crypt -i -ldflags "-s -w -X main.version=${VERSION}" 12 | ${GOPATH}/bin/goveralls -coverprofile=main.coverprofile -service travis-ci || true 13 | 14 | ssh-crypt.exe: ssh-crypt 15 | env GOOS=windows go build -o ssh-crypt.exe -i -ldflags "-s -w -X main.version=${VERSION}" 16 | 17 | ssh-crypt_darwin: ssh-crypt 18 | env GOOS=darwin go build -o ssh-crypt_darwin -i -ldflags "-s -w -X main.version=${VERSION}" 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project is deprecated, now you can just use [FiloSottile/age](https://github.com/FiloSottile/age#ssh-keys) 2 | 3 | # ssh-crypt 🔒 [![Build Status](https://travis-ci.org/suside/ssh-crypt.svg?branch=master)](https://travis-ci.org/suside/ssh-crypt) [![Coverage Status](https://coveralls.io/repos/github/suside/ssh-crypt/badge.svg?branch=master)](https://coveralls.io/github/suside/ssh-crypt?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/suside/ssh-crypt)](https://goreportcard.com/report/github.com/suside/ssh-crypt) [![Say thanks!](https://img.shields.io/badge/SayThanks.io-%F0%9F%91%8D-1EAEDB.svg)](https://saythanks.io/to/suside) 4 | 5 | Share AES-256 encrypted vault file with your teammates using only ssh `authorized_keys`! 6 | 7 | ## Usage 8 | ``` 9 | $ echo "secret :)" | ssh-crypt edit --stdin -a ~/.ssh/authorized_keys VAULT.txt 10 | $ cat VAULT.txt 11 | dRAALGdpdGh1Yi5jb20vc3Vza... 12 | $ ssh-crypt view VAULT.txt 13 | secret :) 14 | ``` 15 | 16 | ## Install 17 | 18 | Download binary release https://github.com/suside/ssh-crypt/releases/latest 19 | or install with `go` from master branch: 20 | ``` 21 | go get github.com/suside/ssh-crypt 22 | ``` 23 | 24 | ## Why 25 | * Sharing Keepass with one master password is a no go... 26 | * Not everyone have/want pgp keys... 27 | * ... 28 | 29 | ## Inspiration 30 | This is cheeky rewrite of great [ssh-vault](https://github.com/ssh-vault/ssh-vault) with less features **but** with support of multiple key pairs. 31 | -------------------------------------------------------------------------------- /lib/key_read.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "crypto/rsa" 5 | "crypto/x509" 6 | "encoding/pem" 7 | "errors" 8 | "io/ioutil" 9 | "strings" 10 | 11 | ssh "github.com/ianmcmahon/encoding_ssh" 12 | ) 13 | 14 | // ReadAuthorizedKeys file from path 15 | func (v *Vault) ReadAuthorizedKeys(path string) { 16 | authorizedKeys, _ := ioutil.ReadFile(path) 17 | authorizedKeysList := strings.Split(strings.TrimSpace(string(authorizedKeys)), "\n") 18 | for _, authorizedKey := range authorizedKeysList { 19 | if pubKey, err := ssh.DecodePublicKey(authorizedKey); err == nil { 20 | // TODO handle other key types 21 | v.publicKeys = append(v.publicKeys, pubKey.(*rsa.PublicKey)) 22 | } 23 | } 24 | } 25 | 26 | func (v *Vault) readPrivateKey(path string) error { 27 | pemData, err := ioutil.ReadFile(path) 28 | if err != nil { 29 | return err 30 | } 31 | block, _ := pem.Decode(pemData) 32 | if block == nil { 33 | return errors.New("Unable to decode private key") 34 | } 35 | if v.privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { 36 | return err 37 | } 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /lib/serialize.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rsa" 6 | "encoding/base64" 7 | "encoding/gob" 8 | "hash/crc32" 9 | ) 10 | 11 | func init() { 12 | gob.Register([]*rsa.PublicKey{}) 13 | gob.Register(rsa.PublicKey{}) 14 | gob.Register(rsa.PrivateKey{}) 15 | gob.Register(vaultSecured{}) 16 | } 17 | 18 | func toBytes(v interface{}) []byte { 19 | b := bytes.Buffer{} 20 | e := gob.NewEncoder(&b) 21 | e.Encode(&v) 22 | return b.Bytes() 23 | } 24 | 25 | func toBase64(v interface{}) string { 26 | return base64.StdEncoding.EncodeToString(toBytes(v)) 27 | } 28 | 29 | func fromBase64(str string) (interface{}, error) { 30 | var m interface{} 31 | by, _ := base64.StdEncoding.DecodeString(str) 32 | b := bytes.Buffer{} 33 | b.Write(by) 34 | d := gob.NewDecoder(&b) 35 | if err := d.Decode(&m); err != nil { 36 | return m, err 37 | } 38 | return m, nil 39 | } 40 | 41 | func crc32sum(v interface{}) uint32 { 42 | return crc32.Checksum(toBytes(v), crc32.IEEETable) 43 | } 44 | -------------------------------------------------------------------------------- /lib/vault.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import "crypto/rsa" 4 | 5 | // Vault struct 6 | type Vault struct { 7 | Plaintext []byte 8 | sessionKey []byte 9 | publicKeys []*rsa.PublicKey 10 | privateKey *rsa.PrivateKey 11 | } 12 | 13 | type vaultSecured struct { 14 | EncryptedData []byte 15 | EncryptedSessionKeys map[uint32][]byte 16 | } 17 | -------------------------------------------------------------------------------- /lib/vault_read.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/sha1" 7 | "fmt" 8 | "io/ioutil" 9 | "os" 10 | 11 | "github.com/ssh-vault/crypto/aead" 12 | ) 13 | 14 | // DecryptVaultWithKey decrypts vault with private id_rsa key read from path 15 | func (v *Vault) DecryptVaultWithKey(vaultPath string, keyPath string) error { 16 | var encryptedData []byte 17 | var err error 18 | var vsBase interface{} 19 | if err = v.readPrivateKey(keyPath); err != nil { 20 | return err 21 | } 22 | if _, err = os.Stat(vaultPath); os.IsNotExist(err) { 23 | v.Plaintext = []byte("") 24 | return nil 25 | } 26 | if encryptedData, err = ioutil.ReadFile(vaultPath); err != nil { 27 | return err 28 | } 29 | vsBase, err = fromBase64(string(encryptedData)) 30 | if err != nil { 31 | return fmt.Errorf("%s content does not look like base64", vaultPath) 32 | } 33 | vs, ok := vsBase.(vaultSecured) 34 | if !ok { 35 | return fmt.Errorf("%s does not look like a vault file", vaultPath) 36 | } 37 | sessionKey, _ := rsa.DecryptOAEP( 38 | sha1.New(), 39 | rand.Reader, 40 | v.privateKey, 41 | vs.EncryptedSessionKeys[crc32sum(v.privateKey.Public())], 42 | []byte(""), 43 | ) 44 | if sessionKey != nil { 45 | v.Plaintext, _ = aead.Decrypt(sessionKey, vs.EncryptedData, []byte("")) 46 | return nil 47 | } 48 | return fmt.Errorf("Unable to read vault %s with key %s", vaultPath, keyPath) 49 | } 50 | 51 | // ReadStdIn content from stdin? 52 | func (v *Vault) ReadStdIn() { 53 | v.Plaintext, _ = ioutil.ReadAll(os.Stdin) 54 | } 55 | -------------------------------------------------------------------------------- /lib/vault_test.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "errors" 5 | "io/ioutil" 6 | "os" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func Test_readAuthorizedKeys(t *testing.T) { 13 | v := Vault{} 14 | v.ReadAuthorizedKeys("../test/authorized_keys") 15 | assert.Equal(t, uint32(0x752098b2), crc32sum(v.publicKeys[0])) 16 | assert.Equal(t, uint32(0xbc02a9b3), crc32sum(v.publicKeys[1])) 17 | } 18 | 19 | func Test_readPrivateKey(t *testing.T) { 20 | v := Vault{} 21 | v.readPrivateKey("../test/t1_id_rsa") 22 | assert.Equal(t, uint32(0xaa0afb16), crc32sum(v.privateKey)) 23 | } 24 | 25 | func Test_storeAndReadVault(t *testing.T) { 26 | tmpfile, _ := ioutil.TempFile("", "ssh-crypt-test") 27 | defer os.Remove(tmpfile.Name()) 28 | v1 := Vault{Plaintext: []byte("my secret")} 29 | v1.ReadAuthorizedKeys("../test/authorized_keys") 30 | v1.StoreSecuredVault(tmpfile.Name()) 31 | 32 | v2 := Vault{} 33 | v2.DecryptVaultWithKey(tmpfile.Name(), "../test/t1_id_rsa") 34 | v3 := Vault{} 35 | v3.DecryptVaultWithKey(tmpfile.Name(), "../test/t2_id_rsa") 36 | assert.Equal(t, v2.Plaintext, v3.Plaintext) 37 | } 38 | 39 | func Test_storeAndReadVaultWithNotUsedKey(t *testing.T) { 40 | tmpfile, _ := ioutil.TempFile("", "ssh-crypt-test") 41 | defer os.Remove(tmpfile.Name()) 42 | v1 := Vault{Plaintext: []byte("my secret")} 43 | v1.ReadAuthorizedKeys("../test/authorized_keys") 44 | v1.StoreSecuredVault(tmpfile.Name()) 45 | 46 | v3 := Vault{} 47 | err := v3.DecryptVaultWithKey(tmpfile.Name(), "../test/t3_id_rsa") 48 | assert.Equal(t, "Unable to read vault "+tmpfile.Name()+" with key ../test/t3_id_rsa", err.Error()) 49 | } 50 | 51 | func Test_EncodingVaultSecured(t *testing.T) { 52 | v := vaultSecured{} 53 | v1, err := fromBase64(toBase64(v)) 54 | assert.Equal(t, v, v1) 55 | assert.Nil(t, err) 56 | } 57 | 58 | func Test_ReadWithKeyEmptyVault(t *testing.T) { 59 | v := Vault{} 60 | v.DecryptVaultWithKey("/tmp/newvault123123", "../test/t1_id_rsa") 61 | assert.Equal(t, []byte(""), v.Plaintext) 62 | } 63 | 64 | func Test_ReadWithKeyNotBase64(t *testing.T) { 65 | v := Vault{} 66 | assert.Equal( 67 | t, 68 | "../test/authorized_keys content does not look like base64", 69 | v.DecryptVaultWithKey("../test/authorized_keys", "../test/t1_id_rsa").Error(), 70 | ) 71 | } 72 | 73 | func Test_ReadWithKeyNoKey(t *testing.T) { 74 | v := Vault{} 75 | assert.Equal( 76 | t, 77 | "no such file", 78 | v.DecryptVaultWithKey("../test/authorized_keys", "no such file").(*os.PathError).Path, 79 | ) 80 | } 81 | 82 | func Test_readPrivateKeyNoKey(t *testing.T) { 83 | v := Vault{} 84 | assert.Equal(t, "no such file", v.readPrivateKey("no such file").(*os.PathError).Path) 85 | } 86 | 87 | func Test_readPrivateKeyFail2(t *testing.T) { 88 | v := Vault{} 89 | assert.Equal(t, errors.New("Unable to decode private key"), v.readPrivateKey("../test/authorized_keys")) 90 | } 91 | -------------------------------------------------------------------------------- /lib/vault_write.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/sha1" 7 | "io/ioutil" 8 | "os" 9 | 10 | "os/exec" 11 | 12 | "github.com/ssh-vault/crypto/aead" 13 | ) 14 | 15 | // StoreSecuredVault encrypts Vault and stores it on vaultPath 16 | func (v *Vault) StoreSecuredVault(vaultPath string) error { 17 | var err error 18 | v.sessionKey = make([]byte, 32) 19 | rand.Read(v.sessionKey) 20 | vs := vaultSecured{EncryptedSessionKeys: make(map[uint32][]byte)} 21 | for _, key := range v.publicKeys { 22 | sessionKeySecure, _ := rsa.EncryptOAEP( 23 | sha1.New(), 24 | rand.Reader, 25 | key, 26 | v.sessionKey, 27 | []byte(""), 28 | ) 29 | vs.EncryptedSessionKeys[crc32sum(&key)] = sessionKeySecure 30 | } 31 | vs.EncryptedData, err = aead.Encrypt(v.sessionKey, v.Plaintext, []byte("")) 32 | if err != nil { 33 | return err 34 | } 35 | ioutil.WriteFile(vaultPath, []byte(toBase64(vs)), 0644) 36 | return nil 37 | } 38 | 39 | // EditVaultFile vault.Path file 40 | func (v *Vault) EditVaultFile() { 41 | tmpfile, _ := ioutil.TempFile("", "ssh-crypt") 42 | defer os.Remove(tmpfile.Name()) 43 | tmpfile.Write([]byte(v.Plaintext)) 44 | editor := os.Getenv("EDITOR") 45 | if editor == "" { 46 | editor = "vi" 47 | } 48 | cmd := exec.Command(editor, tmpfile.Name()) 49 | cmd.Stdin = os.Stdin 50 | cmd.Stdout = os.Stdout 51 | cmd.Run() 52 | v.Plaintext, _ = ioutil.ReadFile(tmpfile.Name()) 53 | } 54 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "strings" 8 | 9 | "github.com/suside/ssh-crypt/lib" 10 | 11 | "gopkg.in/alecthomas/kingpin.v2" 12 | ) 13 | 14 | var ( 15 | idRsaDefault = os.ExpandEnv( 16 | strings.Join([]string{"$HOME", ".ssh", "id_rsa"}, string(os.PathSeparator))) 17 | authKeysDefault = os.ExpandEnv( 18 | strings.Join([]string{"$HOME", ".ssh", "authorized_keys"}, string(os.PathSeparator))) 19 | 20 | version = "master" 21 | app = kingpin.New( 22 | "ssh-crypt", 23 | fmt.Sprintf("Encrypt file with your ssh keys. (%s)", version), 24 | ) 25 | idRsaPath = app.Flag( 26 | "identity-file", 27 | "File from which the identity (private key) is read.", 28 | ).Short('i').Default(idRsaDefault).ExistingFile() 29 | 30 | edit = app.Command("edit", "Edit vault content") 31 | vaultFileE = edit.Arg( 32 | "vault-file", 33 | "File where encrypted content is stored.", 34 | ).Required().String() 35 | authKeysPath = edit.Flag( 36 | "authorized-keys", 37 | "File from which the public keys are read.", 38 | ).Short('a').Default(authKeysDefault).ExistingFile() 39 | fromStdin = edit.Flag( 40 | "stdin", 41 | "Read from standard input instead from $EDITOR", 42 | ).Short('s').Bool() 43 | 44 | view = app.Command("view", "Print vault content") 45 | vaultFileV = view.Arg( 46 | "vault-file", 47 | "File where encrypted content is stored.", 48 | ).Required().String() 49 | ) 50 | 51 | func main() { 52 | switch kingpin.MustParse(app.Parse(os.Args[1:])) { 53 | case edit.FullCommand(): 54 | vault := lib.Vault{} 55 | vault.ReadAuthorizedKeys(*authKeysPath) 56 | if *fromStdin { 57 | vault.ReadStdIn() 58 | } else { 59 | err := vault.DecryptVaultWithKey(*vaultFileE, *idRsaPath) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | vault.EditVaultFile() 64 | } 65 | vault.StoreSecuredVault(*vaultFileE) 66 | case view.FullCommand(): 67 | vault := lib.Vault{} 68 | err := vault.DecryptVaultWithKey(*vaultFileV, *idRsaPath) 69 | if err != nil { 70 | log.Fatal(err) 71 | } 72 | fmt.Print(string(vault.Plaintext)) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/authorized_keys: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqtNbzdINp4hzzLgq1We3H3EjtyBVbxdlZpcj4oWyLclcVn0vrMaivXMukBoLYzFofhlQoryLdcwHK+XuiiBbtHUY2enKWvPx+jBD+htpyWXIJDq1fN7Zq0Ds4SUD2WDfhCFBwgCSqCLZNsSRPOu0Gv+eZ0ceLFmpecubJndFmJ8zx2hWL7kfYUiLUxAPHIhzYke/iU7GEMuKVl/rY0RwKPl9uQtpE2fAUdC17q31owikZg177pGr/7+H4mQafZFLcX1q/R3HU0BSrs5j0erOt7Lm8hI0vK4MuL8TW3jjEl151KEvrrJbRmemXTOGqmfOfprr74+lYJCjeAg/B/+pn su@avalanche 2 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC266rLiwyfrn0KCxhA0CXeOSwW4ogE/Y32W5SGt3llvhZJtikOGFDtoLs7DzMSsQiDztufxe0HfQHEoQxu+2uEfEKrvaAO1Yzi1Mx0/ymoII8C2g7jYel41RXdPxjmteFB3LNhXVqOKUkYYpH1XezlACVQHfM+3+1s9VpXkNmNNVr64AXboqiRnKoN3oNjsObYeA0s9YUziglNFV7Td5jFmBCXI5UCIjhYJAD24zq1v0suGmZ/Teoc5cr2VIHtN3wVb9f5ANfT+UARbDXlTOwk9G8Dxml64qTy9LKA3JA+oe1ej2gP+zizljBKXiudGVxkkFL38YQ47iSiDf1u1HVP su@avalanche 3 | -------------------------------------------------------------------------------- /test/base64.txt: -------------------------------------------------------------------------------- 1 | ERAABnN0cmluZwwGAAR0ZXN0 -------------------------------------------------------------------------------- /test/t1_id_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAqrTW83SDaeIc8y4KtVntx9xI7cgVW8XZWaXI+KFsi3JXFZ9L 3 | 6zGor1zLpAaC2MxaH4ZUKK8i3XMByvl7oogW7R1GNnpylrz8fowQ/obacllyCQ6t 4 | Xze2atA7OElA9lg34QhQcIAkqgi2TbEkTzrtBr/nmdHHixZqXnLmyZ3RZifM8doV 5 | i+5H2FIi1MQDxyIc2JHv4lOxhDLilZf62NEcCj5fbkLaRNnwFHQte6t9aMIpGYNe 6 | +6Rq/+/h+JkGn2RS3F9av0dx1NAUq7OY9Hqzrey5vISNLyuDLi/E1t44xJdedShL 7 | 66yW0Znpl0zhqpnzn6a6++PpWCQo3gIPwf/qZwIDAQABAoIBAG4Jdgmm0FL/exPS 8 | WPq/hkPvnl8QO5xVs55S/HIwc+yLftEkRubLsuWfVnFb9RpU1788cqFrC3ld+gjh 9 | uq1xNf4QgcL6OUUre0MDfKzlNojBWX23JVCcdAz0P0gwCB0DjrmTB28NWxTz2rfH 10 | +RTNch6Is3z2ub7Ray2IuXW7MlC7R5fkX6+dW1JF5bB0clBJsrfcdOfsc1hkipbV 11 | WFqbUmkizvC2j24jVZ1Coo0ngw/WMIiozzFZm+yLm19P3I23TWUMQk410lfLIMB7 12 | 4S3USGm377W6Yf0SEZYOoyATT2f5EUkyZypkhxwso9wu+s1gBaC/ssl1IGgitnQy 13 | W7V048kCgYEA1J3DMWJC1Wlg7AjaddAaD933lP+yaoeAgqfsnz4gXeHsM8WaIxxd 14 | 9rOPgcmZD5oaShT0CJgAdv39LIIBKsl59yTtz5wvlV5/QkUKGscXLJ2cLsjOJxgV 15 | PAtFj6rINJj+zebdUofb8LBBSsEFnhhYdWqdWQplfnWsrpNG7Y1Kg2UCgYEAzYnf 16 | AnAj0KFp6kaz+XNpHKV6h6dG+7r0J6j5kMxaaE9yx0FjstIy7gM5Wktc0BD0mWcZ 17 | AcSZkJmqkoplP1LHxItiWWaWZPUamcq5pozPoOQWYiirV1mLpE5xE1LLPxhN8Q7W 18 | TQq2WJJQ7iTT3yN8+hKbwxOLe8QR0D1oDAvGx9sCgYAF4btloVdB+iFRRkls2u3t 19 | pHFV8ODpq4f1lVmpahmRH0/eesfx0rYSvhLpPz95vASgKgY6OqJAqHQZiBsqKNqz 20 | 6JSfmq2CT1Pr7GcEKJ/Ofymz7aAuWq8zswggmlPhiJ376jgTMWj3NpkHzWEvZ3MD 21 | BIkHzo7SOcUmKzLaHX+DNQKBgQCqjDhL4ZkWSJmGF9zGpAkYCAdAAIFZ56soLYHH 22 | sIlj/GiVBaEghGJWNL6mOdptm2Qa1f1eBjAWWCu9JLEWdi3k3HNjE0i/xNGpwxmB 23 | JV4zdo9MK8rg1CCW2a/iBNU5KYWi/Fi93KGppN8ae9yCTwQozMAKgnE+5VaSoCFx 24 | vWPX+QKBgQCdD9WqEmntCj0GCv4KEUf2EKaFnd7DduVsNBv1DLwDXoUfpox+dV+d 25 | VpQ5OHPWIJnJ2slAwBBwh8IUYfowB2k8BQEAynBSdQWnPaj1PGyMzO8bsUyvJLO0 26 | 2A3b7yUclCDsklkNHM7EYv2gVYdyRcOibMLY+6DZDP88ietxgMglEA== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/t1_id_rsa.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqtNbzdINp4hzzLgq1We3H3EjtyBVbxdlZpcj4oWyLclcVn0vrMaivXMukBoLYzFofhlQoryLdcwHK+XuiiBbtHUY2enKWvPx+jBD+htpyWXIJDq1fN7Zq0Ds4SUD2WDfhCFBwgCSqCLZNsSRPOu0Gv+eZ0ceLFmpecubJndFmJ8zx2hWL7kfYUiLUxAPHIhzYke/iU7GEMuKVl/rY0RwKPl9uQtpE2fAUdC17q31owikZg177pGr/7+H4mQafZFLcX1q/R3HU0BSrs5j0erOt7Lm8hI0vK4MuL8TW3jjEl151KEvrrJbRmemXTOGqmfOfprr74+lYJCjeAg/B/+pn su@avalanche 2 | -------------------------------------------------------------------------------- /test/t2_id_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAtuuqy4sMn659CgsYQNAl3jksFuKIBP2N9luUhrd5Zb4WSbYp 3 | DhhQ7aC7Ow8zErEIg87bn8XtB30BxKEMbvtrhHxCq72gDtWM4tTMdP8pqCCPAtoO 4 | 42HpeNUV3T8Y5rXhQdyzYV1ajilJGGKR9V3s5QAlUB3zPt/tbPVaV5DZjTVa+uAF 5 | 26KokZyqDd6DY7Dm2HgNLPWFM4oJTRVe03eYxZgQlyOVAiI4WCQA9uM6tb9LLhpm 6 | f03qHOXK9lSB7Td8FW/X+QDX0/lAEWw15UzsJPRvA8ZpeuKk8vSygNyQPqHtXo9o 7 | D/s4s5YwSl4rnRlcZJBS9/GEOO4kog39btR1TwIDAQABAoIBAQCWZVXcXMtYTyLH 8 | qy+ZuoooGGHDvyWO1NpwAAQyb5AEszGoEXnX+O4xMsI+YNThPWV+2gmBaD7mtUVK 9 | 4g9TAKz6FSDMC/EZwUNBDS291D3v2jU7ZwZutgY8Lxb2/fX/WvOGL2vdVeAqmo9a 10 | 2VCgqUUpU+FZ6gYhYAPaXjCVZqc4jY1O6LMe92Ies+Sc5F3YLUIR9j56KtMM+UtP 11 | OjRG3wHzMQC4oV8vBuJ0097zXllNqJniQAhJ1UMZmzzLOWQmdqk89yrIn6Y1wO9I 12 | j/B4xF9LAD0zrnzCIets3mLSZEgqqeuPAiZquC91uULobFTwDuw89HP6fsUs9CyX 13 | PSsnxD2BAoGBAOca4keA3kXbLgYaOtassBZbCUyOZqGvSquraEWWztMD8CpIRM/L 14 | LlU9y6lD1rrFKYC78UBpl7cFCKMSq8JGYKncnNfVQqgbXXHoTgH/z2Y4FXq5e65U 15 | LS8/Ib3H2QdB2Rkoiu8MUHI+kRsj/YPtF/zcPDOqlr8/KfeIZB0IBOL/AoGBAMqg 16 | A9CtAO0u7PY8AEk/F/NKI9GDA/ccRrMqRKf4IpBYavlSt9BBA84kolNLTQ1sRY8a 17 | QlXyTOBMkbzFsuZOXPhnAGH9uUn6Si3bSDvzYMoE0WJ0nWU7beJVdi5RyhzNzBZI 18 | YtHaVyrOmauTbMXh94v7o+osLg4agOBdobtIY32xAoGBAOZ2lc7ugToyj9wADksE 19 | nPk+QbC5lTmJwDIf1fUHpisikz7nINADc6oH3PBoOVu5wGCji7tLUcNu3qUYunYb 20 | jsv1t+c4wac42t/75MbznpT2Ot1O2TsIsKKcF37VK+Wj0ebX2mz238Z+BGCHbIpC 21 | RxifW92+MWR4jdZOil+1HoJrAoGADAxxzaHL8U6eBpJQzgozaWMaOCYbQUFsLtEv 22 | nfFSVNT9H3A8v+Rtzn89K8jJfyfWBwlfsm6jBZ9n9xqmZa15N8ggLLOCphR8/rcQ 23 | V4hEVB7vOHSIu3FKOnAsg9qXMxlFrnmFbscV8dSxLJOqnyWtq6Q9/fm8j1giG7VV 24 | naZTTDECgYAj+cjWpQdME1BgMxxtcWw23kvRdFg2n+wfUZT3ADR1WQMIA11VeSZ9 25 | w1IknwJE0pydMEKwPzZv0RMjvxRjYpMH50dcTy/DLd+ZB3EX/EbG36vd9z75H7To 26 | DG8dkvR62PDRq3D1oFU9vgubEdy3p2idXVioNUr/mRwQBBI5bGR7hw== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/t2_id_rsa.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC266rLiwyfrn0KCxhA0CXeOSwW4ogE/Y32W5SGt3llvhZJtikOGFDtoLs7DzMSsQiDztufxe0HfQHEoQxu+2uEfEKrvaAO1Yzi1Mx0/ymoII8C2g7jYel41RXdPxjmteFB3LNhXVqOKUkYYpH1XezlACVQHfM+3+1s9VpXkNmNNVr64AXboqiRnKoN3oNjsObYeA0s9YUziglNFV7Td5jFmBCXI5UCIjhYJAD24zq1v0suGmZ/Teoc5cr2VIHtN3wVb9f5ANfT+UARbDXlTOwk9G8Dxml64qTy9LKA3JA+oe1ej2gP+zizljBKXiudGVxkkFL38YQ47iSiDf1u1HVP su@avalanche 2 | -------------------------------------------------------------------------------- /test/t3_id_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAwkg5z+Jw/+OO7eoHRy1r7lHhodkNJxjfjqmcWJzXOvnXZOJu 3 | lkeeofwaB7uGMccDmRVOb0MzHEY4Bgeyisx9NpBTb3qeazOb1awnM+nlrcTPzvcu 4 | enzhCZoPO5DTC2ZXjdZiv5zsfxsvnTQBbr/+54BEoB1QaZ5iAFyuQ8hob0cO2LHa 5 | 4PrM6DzqnrH5wlSc+w5MxF/JemQkWPoOuawcCluWNo8q+9iMnHs4DQoo3GZaQURa 6 | O+KQsemxZe7TydusxZLADvvCh3E0g8q0Tcor5DlVuI+4bLG2zR5T2vJ37pc53hfz 7 | 4fcvzmwO9ZBQK96sh/bj/wfo09WXO/bIXi0CNwIDAQABAoIBABoq1jaMvkGI6chb 8 | nA1XLdFUj/pXnLDmZb32VEuPHGIiIoPBB7oP/Wq95Nejx0hPEMn+l6jeuTSWtJcD 9 | VGXjcv31ZHeAYXP46IVYAZO5bikAeCjdHBJ9XWYIrQKN2A8SArrk5DoArc3U4I3g 10 | 4kyDaZbOrO+P4zPGlFaOJR6WEJLkF9xzWw76wGj9UzClkmTvtg2Nnsk3aMfLtf3g 11 | cgEGRw4/mHMFRo+NkV4n2i94iixZQZ5RaisugUlNlE+2eeiULNM3yFP4qEjIymn8 12 | cDbtyIptkDVjBQanbFO5VUU9ZHrL40kXbctzwn/rDaLm/lX8K7V4VfEOWsKCMCSa 13 | c5tAOVkCgYEA9dLZiX5y7w2ZujsFU9TIEeGH6xF23oc+oV6F5poWymke+ABkR5EM 14 | 4yAK8vl3GdZVjOliUvZJN66tWHXY/3MyAHS6+iUvNkKawMr3OgNB33O0MdJ3eD9z 15 | e1h262310tq9GYuK4GW9kjxNnHmLK5EiI/sKgdA8fakoxJ0K3YHzItsCgYEAylMo 16 | aA3+4RStkQZLjGuldNGNN1xcx9KQKGrS0o3XJqI7pAXqkv/y3/0dHIW751owETDu 17 | sClrKb68sS6mCbq++lHPsdngnB7BiTCbuu0AUu4JktUmpxXEe5MdpiAN95UhyaLl 18 | CtEvQ2JWM6rl3zuCKVGOHYYcXks3fbWOCDgFptUCgYACXt4qLUX+eCsbWKZcgJjN 19 | U1d0NN7olnVMKRUxfBrsKMdbrOVKq2NsbUqtvSog3ahFi5qfIjDikJSrXbeR/xJk 20 | XrvO3bzu0QWdBlgL/ywLc5oxQM5WIoaCkf/TlWPhfKqGLIpST6jcWCiHccSdzvB9 21 | WFZpmhB7Rb5W5wzLxg0KMQKBgGnr2y2Oli8z6iIQj6C2AmsEAdBH1HLwc3QVaMW+ 22 | aDLboCmgx0uqH3bQRltWUCcwehWl7m/fXzD9oD+KUnapg639AqTzkblqCJxQDFdy 23 | GEdSQ0TRvmie5rxjKpR8F5PUSVoMo6hkF0BJzSNDhxC1UNFc1u/x9AwRiL2RPQhk 24 | e+D5AoGAXZv+LYS9zqbOCM5Tp73WjeId3V8oouBxfB8oxsvnfPxrTZGbekEjVUeU 25 | MA7Bbls39knydqEWii8UbcGwNGPfvh+vP2k3cVACF7YvF0TGPwRh2zOKmAWs91ix 26 | yP470BRFkGAV/tl478f/yACWXVjU6QbYqNSXMpF5F2nq1BBWyBA= 27 | -----END RSA PRIVATE KEY----- 28 | --------------------------------------------------------------------------------