github.com/tommi2day/gomodules@v1.13.2-0.20240423190010-b7d55d252a27/pwlib/rsa.go (about)

     1  // Package pwlib passwords encryption functions
     2  package pwlib
     3  
     4  // alternative: https://gist.github.com/wongoo/2b974a9594627114bea3e53c794980cd
     5  import (
     6  	"bytes"
     7  	"crypto/aes"
     8  	"crypto/cipher"
     9  	"crypto/rand"
    10  	"crypto/rsa"
    11  	"crypto/sha256"
    12  	"crypto/x509"
    13  	"encoding/base64"
    14  	"encoding/pem"
    15  	"errors"
    16  	"fmt"
    17  	"io"
    18  	"os"
    19  	"strings"
    20  
    21  	log "github.com/sirupsen/logrus"
    22  )
    23  
    24  // GenRsaKey generate new key pair
    25  func GenRsaKey(pubfilename string, privfilename string, password string) (publicKey *rsa.PublicKey, privateKey *rsa.PrivateKey, err error) {
    26  	bits := defaultRsaKeySize
    27  	privateKey, err = rsa.GenerateKey(rand.Reader, bits)
    28  	if err != nil || privateKey == nil {
    29  		log.Debugf("generate Key failed: %s", err)
    30  		return
    31  	}
    32  	publicKey = &privateKey.PublicKey
    33  
    34  	// save to file if required
    35  	if len(privfilename) > 0 {
    36  		// Convert it to pem
    37  		privbytes, _ := x509.MarshalPKCS8PrivateKey(privateKey)
    38  		block := &pem.Block{
    39  			Type:  "RSA PRIVATE KEY",
    40  			Bytes: privbytes,
    41  		}
    42  
    43  		// Encrypt the pem
    44  		if password != "" {
    45  			//nolint gosec
    46  			block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(password), x509.PEMCipherAES256)
    47  			if err != nil {
    48  				log.Errorf("cannot encrypt private key %s", err)
    49  				return
    50  			}
    51  		}
    52  		// save it
    53  		privatekeyPem := pem.EncodeToMemory(block)
    54  		err = os.WriteFile(privfilename, privatekeyPem, 0600)
    55  		if err != nil {
    56  			log.Errorf("cannot write %s: %s", privfilename, err)
    57  			return
    58  		}
    59  		log.Debugf("private key written to %s", privfilename)
    60  	}
    61  
    62  	if len(pubfilename) > 0 {
    63  		pubbytes, _ := x509.MarshalPKIXPublicKey(publicKey)
    64  		block := &pem.Block{
    65  			Type:  "PUBLIC KEY",
    66  			Bytes: pubbytes,
    67  		}
    68  		pubkeyPem := pem.EncodeToMemory(block)
    69  		//nolint gosec
    70  		err = os.WriteFile(pubfilename, pubkeyPem, 0644)
    71  		if err != nil {
    72  			log.Errorf("cannot write %s: %s", pubfilename, err)
    73  			return
    74  		}
    75  		log.Debugf("public key written to %s", pubfilename)
    76  	}
    77  	log.Debug("Keys generated")
    78  	return
    79  }
    80  
    81  // GetPrivateKeyFromFile  read private key from PEM encoded File and returns publicKey and private key objects
    82  func GetPrivateKeyFromFile(privfilename string, rsaPrivateKeyPassword string) (publicKey *rsa.PublicKey, privateKey *rsa.PrivateKey, err error) {
    83  	var parsedKey interface{}
    84  	var privPemBytes []byte
    85  
    86  	log.Debugf("GetPrivateKeyFromFile entered for %s", privfilename)
    87  	//nolint gosec
    88  	priv, err := os.ReadFile(privfilename)
    89  	if err != nil {
    90  		log.Debugf("cannot read %s: %s", privfilename, err)
    91  		return
    92  	}
    93  	privPem, _ := pem.Decode(priv)
    94  	if privPem == nil {
    95  		log.Debugf("cannot decode pem in %s", privfilename)
    96  		return
    97  	}
    98  	if privPem.Type != "RSA PRIVATE KEY" {
    99  		log.Debugf("rsa private key is of the wrong type %s", privPem.Type)
   100  		return
   101  	}
   102  
   103  	if rsaPrivateKeyPassword != "" {
   104  		//nolint gosec
   105  		privPemBytes, err = x509.DecryptPEMBlock(privPem, []byte(rsaPrivateKeyPassword))
   106  		if err != nil {
   107  			log.Debugf("rsa private password error:%s", err)
   108  			return
   109  		}
   110  	} else {
   111  		privPemBytes = privPem.Bytes
   112  	}
   113  	parsedKey, err = x509.ParsePKCS8PrivateKey(privPemBytes)
   114  	if err != nil {
   115  		if strings.Contains(err.Error(), "use ParsePKCS1PrivateKey") {
   116  			log.Debug("ParsePKCS8PrivateKey failed, trying ParsePKCS1PrivateKey")
   117  			parsedKey, err = x509.ParsePKCS1PrivateKey(privPemBytes)
   118  		}
   119  		if err != nil {
   120  			log.Debugf("unable to parse RSA private key: %s", err)
   121  			return
   122  		}
   123  	}
   124  	privateKey = parsedKey.(*rsa.PrivateKey)
   125  	if privateKey == nil {
   126  		err = fmt.Errorf("unable to cast private key")
   127  		log.Debugf("%s:", err)
   128  		return
   129  	}
   130  	publicKey = &privateKey.PublicKey
   131  	log.Debugf("Keys successfully loaded")
   132  	return
   133  }
   134  
   135  // GetPublicKeyFromFile  read public key from PEM encoded File
   136  func GetPublicKeyFromFile(publicKeyFile string) (publicKey *rsa.PublicKey, err error) {
   137  	var parsedKey interface{}
   138  	log.Debugf("load public key from %s", publicKeyFile)
   139  	//nolint gosec
   140  	pub, err := os.ReadFile(publicKeyFile)
   141  	if err != nil {
   142  		log.Debugf("Cannot Read %s: %s", publicKeyFile, err)
   143  		return
   144  	}
   145  	pubPem, _ := pem.Decode(pub)
   146  	if pubPem == nil {
   147  		log.Debugf("Cannot Decode %s", publicKeyFile)
   148  		return
   149  	}
   150  	if pubPem.Type != "PUBLIC KEY" {
   151  		log.Debugf("RSA public key is of the wrong type %s", pubPem.Type)
   152  		return
   153  	}
   154  
   155  	parsedKey, err = x509.ParsePKIXPublicKey(pubPem.Bytes)
   156  	if err != nil {
   157  		log.Debugf("unable to parse RSA public key: %s", err)
   158  		return
   159  	}
   160  
   161  	publicKey = parsedKey.(*rsa.PublicKey)
   162  	if publicKey == nil {
   163  		err = errors.New("unable to cast public key")
   164  		log.Debugf("%s:", err)
   165  	}
   166  	log.Debugf("public key loaded successfully")
   167  	return
   168  }
   169  
   170  // PubEncryptFileGo encrypts a file with public key with GO API
   171  func PubEncryptFileGo(plainFile string, targetFile string, publicKeyFile string) (err error) {
   172  	const rb = 16
   173  	var plaindata []byte
   174  	log.Debugf("Encrypt %s with public key %s", plainFile, publicKeyFile)
   175  	publicKey, err := GetPublicKeyFromFile(publicKeyFile)
   176  	if err != nil {
   177  		return
   178  	}
   179  	sessionKey := make([]byte, rb)
   180  	_, err = rand.Read(sessionKey)
   181  	if err != nil {
   182  		log.Debugf("Cannot generate session key:%s", err)
   183  		return
   184  	}
   185  	//nolint gosec
   186  	plaindata, err = os.ReadFile(plainFile)
   187  	if err != nil {
   188  		log.Debugf("Cannot read plaintext file %s:%s", plainFile, err)
   189  		return
   190  	}
   191  
   192  	// sha1 for compatibility with python version
   193  	hash := sha256.New()
   194  	// oder rsa.EncryptPKCS1v15()
   195  	encSessionKey, err := rsa.EncryptOAEP(hash, rand.Reader, publicKey, sessionKey, label)
   196  	skl := len(encSessionKey)
   197  	log.Debugf("Session key len: %d", skl)
   198  	if err != nil {
   199  		log.Error(err)
   200  		return
   201  	}
   202  	block, err := aes.NewCipher(sessionKey)
   203  	if err != nil {
   204  		log.Debugf("Cannot create cipher: %s", err.Error())
   205  		return
   206  	}
   207  
   208  	aesgcm, err := cipher.NewGCM(block)
   209  	if err != nil {
   210  		log.Debugf("Cannot create easgcm: %s", err.Error())
   211  		return
   212  	}
   213  	ns := aesgcm.NonceSize()
   214  	// Never use more than 2^32 random nonces with a given key because of the risk of a repeat.
   215  	// must be 12 for GCM
   216  	nonce := make([]byte, ns)
   217  	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
   218  		log.Debugf("Cannot create nounce: %s", err.Error())
   219  		return
   220  	}
   221  
   222  	// do encryption and seal
   223  	cipherdata := aesgcm.Seal(nil, nonce, plaindata, nil)
   224  
   225  	// encode all parts in base64
   226  	bindata := bytes.Join([][]byte{encSessionKey, nonce, cipherdata}, []byte(""))
   227  	b64 := base64.StdEncoding.EncodeToString(bindata)
   228  
   229  	// write crypted output file
   230  	//nolint gosec
   231  	err = os.WriteFile(targetFile, []byte(b64), 0644)
   232  	if err != nil {
   233  		log.Debugf("Cannot write: %s", err.Error())
   234  		return
   235  	}
   236  	return
   237  }
   238  
   239  // PrivateDecryptFileGo Decrypt a file with private key with GO API
   240  func PrivateDecryptFileGo(cryptedfile string, privatekeyfile string, keypass string) (content string, err error) {
   241  	log.Debugf("decrypt %s with private key %s", cryptedfile, privatekeyfile)
   242  	//nolint gosec
   243  	data, err := os.ReadFile(cryptedfile)
   244  	if err != nil {
   245  		log.Debugf("Cannot Read file '%s': %s", cryptedfile, err)
   246  		return
   247  	}
   248  	_, privkey, err := GetPrivateKeyFromFile(privatekeyfile, keypass)
   249  	if err != nil {
   250  		log.Debugf("Cannot read keys from '%s': %s", privatekeyfile, err)
   251  		return
   252  	}
   253  	bindata, err := base64.StdEncoding.DecodeString(string(data))
   254  	if err != nil {
   255  		log.Debugf("decode base64 for %s failed: %s", cryptedfile, err)
   256  		return
   257  	}
   258  	s := 0
   259  	e := privkey.Size()
   260  	encSessionKey := bindata[s:e]
   261  
   262  	hash := sha256.New()
   263  	// oder rsa.EncryptPKCS1v15()
   264  	sessionKey, err := rsa.DecryptOAEP(hash, rand.Reader, privkey, encSessionKey, label)
   265  	if err != nil {
   266  		log.Debugf("decode session key failed:%s", err)
   267  		return
   268  	}
   269  	// sk := string(sessionKey)
   270  	log.Debug("Session key decrypted")
   271  
   272  	block, err := aes.NewCipher(sessionKey)
   273  	if err != nil {
   274  		log.Debugf("Cannot init cipher:%s", err)
   275  		return
   276  	}
   277  
   278  	aesgcm, err := cipher.NewGCM(block)
   279  	if err != nil {
   280  		log.Debugf("Cannot setup AESGCM:%s", err)
   281  		return
   282  	}
   283  
   284  	// split parts
   285  	ns := aesgcm.NonceSize()
   286  	s = e
   287  	e = s + ns
   288  	nonce := bindata[s:e]
   289  	s = e
   290  	cipherdata := bindata[s:]
   291  
   292  	// do decrypt
   293  	plaindata, err := aesgcm.Open(nil, nonce, cipherdata, nil)
   294  	if err != nil {
   295  		log.Debugf("Cannot decode crypted data:%s", err)
   296  		return
   297  	}
   298  	// return content
   299  	content = string(plaindata)
   300  	log.Debug("Decoding successfully")
   301  	return
   302  }
   303  
   304  // PrivateDecryptString Decrypt a string with private key
   305  func PrivateDecryptString(crypted string, privatekeyfile string, keypass string) (plain string, err error) {
   306  	// echo -n "$CRYPTED"|base64 -d  |openssl rsautl -decrypt -inkey ${PRIVATEKEYFILE} -passin pass:$KEYPASS
   307  	log.Debugf("decrypt string with private key %s", privatekeyfile)
   308  	_, privkey, err := GetPrivateKeyFromFile(privatekeyfile, keypass)
   309  	if err != nil {
   310  		log.Debugf("Cannot read keys from '%s': %s", privatekeyfile, err)
   311  		return
   312  	}
   313  	b64dec, err := base64.StdEncoding.DecodeString(crypted)
   314  	if err != nil {
   315  		log.Debugf("decode base64 failed: %s", err)
   316  		return
   317  	}
   318  
   319  	data, err := rsa.DecryptPKCS1v15(rand.Reader, privkey, b64dec)
   320  	if err != nil {
   321  		log.Debugf("decode session key failed:%s", err)
   322  		return
   323  	}
   324  	plain = string(data)
   325  	return
   326  }
   327  
   328  // PublicEncryptString  Encrypt a string with public key
   329  func PublicEncryptString(plain string, publicKeyFile string) (crypted string, err error) {
   330  	// echo -n "$plain"|openssl rsautl -encrypt -pkcs -inkey $PUBLICKEYFILE -pubin |base64
   331  	log.Debugf("encrypt string with public key %s", publicKeyFile)
   332  	pubkey, err := GetPublicKeyFromFile(publicKeyFile)
   333  	if err != nil {
   334  		log.Debugf("Cannot read keys from '%s': %s", publicKeyFile, err)
   335  		return
   336  	}
   337  
   338  	data, err := rsa.EncryptPKCS1v15(rand.Reader, pubkey, []byte(plain))
   339  	if err != nil {
   340  		log.Debugf("decode session key failed:%s", err)
   341  		return
   342  	}
   343  	crypted = base64.StdEncoding.EncodeToString(data)
   344  	if err != nil {
   345  		log.Debugf("decode base64 failed: %s", err)
   346  		return
   347  	}
   348  	return
   349  }