github.com/influx6/npkg@v0.8.8/ncrypt/ssh/ssh.go (about)

     1  package ssh
     2  
     3  import (
     4  	"crypto/md5"
     5  	"crypto/rand"
     6  	"crypto/rsa"
     7  	"crypto/x509"
     8  	"encoding/base64"
     9  	"encoding/pem"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  	"runtime"
    15  
    16  	gosh "golang.org/x/crypto/ssh"
    17  )
    18  
    19  // nerror
    20  var (
    21  	ErrKeyGeneration     = errors.New("Unable to generate key")
    22  	ErrValidation        = errors.New("Unable to validate key")
    23  	ErrPublicKey         = errors.New("Unable to convert public key")
    24  	ErrUnableToWriteFile = errors.New("Unable to write file")
    25  )
    26  
    27  type KeyPair struct {
    28  	PrivateKey []byte
    29  	PublicKey  []byte
    30  }
    31  
    32  // NewKeyPair generates a new SSH keypair
    33  // This will return a private & public key encoded as DER.
    34  func NewKeyPair() (keyPair *KeyPair, err error) {
    35  	priv, err := rsa.GenerateKey(rand.Reader, 2048)
    36  	if err != nil {
    37  		return nil, ErrKeyGeneration
    38  	}
    39  
    40  	if err := priv.Validate(); err != nil {
    41  		return nil, ErrValidation
    42  	}
    43  
    44  	privDer := x509.MarshalPKCS1PrivateKey(priv)
    45  
    46  	pubSSH, err := gosh.NewPublicKey(&priv.PublicKey)
    47  	if err != nil {
    48  		return nil, ErrPublicKey
    49  	}
    50  
    51  	return &KeyPair{
    52  		PrivateKey: privDer,
    53  		PublicKey:  gosh.MarshalAuthorizedKey(pubSSH),
    54  	}, nil
    55  }
    56  
    57  // WriteToFile writes keypair to files
    58  func (kp *KeyPair) WriteToFile(privateKeyPath string, publicKeyPath string) error {
    59  	files := []struct {
    60  		File  string
    61  		Type  string
    62  		Value []byte
    63  	}{
    64  		{
    65  			File:  privateKeyPath,
    66  			Value: pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Headers: nil, Bytes: kp.PrivateKey}),
    67  		},
    68  		{
    69  			File:  publicKeyPath,
    70  			Value: kp.PublicKey,
    71  		},
    72  	}
    73  
    74  	for _, v := range files {
    75  		f, err := os.Create(v.File)
    76  		if err != nil {
    77  			return ErrUnableToWriteFile
    78  		}
    79  
    80  		if _, err := f.Write(v.Value); err != nil {
    81  			return ErrUnableToWriteFile
    82  		}
    83  
    84  		// windows does not support chmod
    85  		switch runtime.GOOS {
    86  		case "darwin", "linux", "freebsd":
    87  			if err := f.Chmod(0600); err != nil {
    88  				return err
    89  			}
    90  		}
    91  	}
    92  
    93  	return nil
    94  }
    95  
    96  // Fingerprint calculates the fingerprint of the public key
    97  func (kp *KeyPair) Fingerprint() string {
    98  	b, _ := base64.StdEncoding.DecodeString(string(kp.PublicKey))
    99  	h := md5.New()
   100  
   101  	io.WriteString(h, string(b))
   102  
   103  	return fmt.Sprintf("%x", h.Sum(nil))
   104  }
   105  
   106  // GenerateSSHKey generates SSH keypair based on path of the private key
   107  // The public key would be generated to the same path with ".pub" added
   108  func GenerateSSHKey(path string) error {
   109  	if _, err := os.Stat(path); err != nil {
   110  		if !os.IsNotExist(err) {
   111  			return fmt.Errorf("Desired directory for SSH keys does not exist: %s", err)
   112  		}
   113  
   114  		kp, err := NewKeyPair()
   115  		if err != nil {
   116  			return fmt.Errorf("Error generating key pair: %s", err)
   117  		}
   118  
   119  		if err := kp.WriteToFile(path, fmt.Sprintf("%s.pub", path)); err != nil {
   120  			return fmt.Errorf("Error writing keys to file(s): %s", err)
   121  		}
   122  	}
   123  
   124  	return nil
   125  }