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