github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/util/user/user.go (about)

     1  package user
     2  
     3  import (
     4  	"os/user"
     5  	"path/filepath"
     6  
     7  	"crypto/rand"
     8  	"crypto/rsa"
     9  	"crypto/x509"
    10  	"encoding/base64"
    11  	"encoding/pem"
    12  	"io/ioutil"
    13  	"os"
    14  
    15  	"github.com/tickoalcantara12/micro/v3/service/logger"
    16  )
    17  
    18  var (
    19  	Dir  = ""
    20  	path = ".micro"
    21  )
    22  
    23  func init() {
    24  	user, err := user.Current()
    25  	if err != nil {
    26  		logger.Fatalf(err.Error())
    27  	}
    28  	Dir = filepath.Join(user.HomeDir, path)
    29  	err = os.MkdirAll(Dir, 0700)
    30  	if err != nil {
    31  		logger.Fatalf(err.Error())
    32  	}
    33  }
    34  
    35  // GetConfigSecretKey returns local keys or generates and returns them for
    36  // config secret encoding/decoding.
    37  func GetConfigSecretKey() (string, error) {
    38  	key := filepath.Join(Dir, "config_secret_key")
    39  	if !fileExists(key) {
    40  		err := setupConfigSecretKey(key)
    41  		if err != nil {
    42  			return "", err
    43  		}
    44  	}
    45  	logger.Debugf("Loading config key from %v", key)
    46  	dat, err := ioutil.ReadFile(key)
    47  	if err != nil {
    48  		return "", err
    49  	}
    50  	return string(dat), nil
    51  }
    52  
    53  func setupConfigSecretKey(path string) error {
    54  	logger.Debugf("Setting up config key to %v", path)
    55  	bytes := make([]byte, 32) //generate a random 32 byte key for AES-256
    56  	if _, err := rand.Read(bytes); err != nil {
    57  		return err
    58  	}
    59  	file, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0644)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	file.Close()
    64  
    65  	err = ioutil.WriteFile(path, []byte(base64.StdEncoding.EncodeToString(bytes)), 0600)
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	return nil
    71  }
    72  
    73  // GetJWTCerts returns local keys or generates and returns them for JWT auth.GetJWTCerts
    74  // This is only here for "0 dep", so people don't have to create and load the certs themselves,
    75  // not really intended for serious production use.
    76  func GetJWTCerts() ([]byte, []byte, error) {
    77  	privKey := filepath.Join(Dir, "id_rsa")
    78  	pubKey := filepath.Join(Dir, "id_rsa.pub")
    79  
    80  	logger.Debugf("Loading keys %v and %v", privKey, pubKey)
    81  	if !fileExists(privKey) || !fileExists(pubKey) {
    82  		err := setupKeys(privKey, pubKey)
    83  		if err != nil {
    84  			return nil, nil, err
    85  		}
    86  	}
    87  	privDat, err := ioutil.ReadFile(privKey)
    88  	if err != nil {
    89  		return nil, nil, err
    90  	}
    91  	pubDat, err := ioutil.ReadFile(pubKey)
    92  	if err != nil {
    93  		return nil, nil, err
    94  	}
    95  	return privDat, pubDat, nil
    96  }
    97  
    98  func setupKeys(privKey, pubKey string) error {
    99  	logger.Infof("Setting up keys for JWT at %v and %v", privKey, pubKey)
   100  	bitSize := 4096
   101  	privateKey, err := generatePrivateKey(bitSize)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	publicKeyBytes, err := generatePublicKey(&privateKey.PublicKey)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	privateKeyBytes, err := encodePrivateKeyToPEM(privateKey)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	err = writeKeyToFile(privateKeyBytes, privKey)
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	err = writeKeyToFile([]byte(publicKeyBytes), pubKey)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	return nil
   126  }
   127  
   128  func fileExists(filename string) bool {
   129  	info, err := os.Stat(filename)
   130  	if os.IsNotExist(err) {
   131  		return false
   132  	}
   133  	return !info.IsDir()
   134  }
   135  
   136  // taken from https://gist.github.com/devinodaniel/8f9b8a4f31573f428f29ec0e884e6673
   137  
   138  // generatePrivateKey creates a RSA Private Key of specified byte size
   139  func generatePrivateKey(bitSize int) (*rsa.PrivateKey, error) {
   140  	// Private Key generation
   141  	privateKey, err := rsa.GenerateKey(rand.Reader, bitSize)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	// Validate Private Key
   147  	err = privateKey.Validate()
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	return privateKey, nil
   153  }
   154  
   155  // encodePrivateKeyToPEM encodes Private Key from RSA to PEM format
   156  func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) ([]byte, error) {
   157  	// Get ASN.1 DER format
   158  	privDER := x509.MarshalPKCS1PrivateKey(privateKey)
   159  
   160  	// pem.Block
   161  	privBlock := pem.Block{
   162  		Type:    "RSA PRIVATE KEY",
   163  		Headers: nil,
   164  		Bytes:   privDER,
   165  	}
   166  
   167  	// Private key in PEM format
   168  	privatePEM := pem.EncodeToMemory(&privBlock)
   169  
   170  	return privatePEM, nil
   171  }
   172  
   173  func generatePublicKey(publickey *rsa.PublicKey) ([]byte, error) {
   174  	pubDER, err := x509.MarshalPKIXPublicKey(publickey)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	pubBlock := pem.Block{
   179  		Type:    "PUBLIC KEY",
   180  		Headers: nil,
   181  		Bytes:   pubDER,
   182  	}
   183  	pubPEM := pem.EncodeToMemory(&pubBlock)
   184  
   185  	return pubPEM, nil
   186  }
   187  
   188  // writePemToFile writes keys to a file
   189  func writeKeyToFile(keyBytes []byte, saveFileTo string) error {
   190  	file, err := os.OpenFile(saveFileTo, os.O_RDONLY|os.O_CREATE, 0644)
   191  	if err != nil {
   192  		return err
   193  	}
   194  	file.Close()
   195  
   196  	err = ioutil.WriteFile(saveFileTo, []byte(base64.StdEncoding.EncodeToString(keyBytes)), 0600)
   197  	if err != nil {
   198  		return err
   199  	}
   200  
   201  	return nil
   202  }