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 }