Monday, February 7, 2022

A-Symmetric Encrypt and Decrypt on Golang

 



In this post we will review the steps to use RSA encryption/decryption on GO. 


This post is about using a-symmetric encryption for encrypt/decrypt. In case of need of a-symmetric encryption for sign/verify, check this post.


We start by creation a struct to represent the encryption:


package encryption

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"hash"
"io"
"os"
"radware.com/euda/commons/global/log"
)

type Key struct {
privateKey *rsa.PrivateKey
hash hash.Hash
random io.Reader
}

func (k *Key) init() {
k.hash = sha256.New()
k.random = rand.Reader
}


We can generate a new key, which takes about 5 seconds on my desktop machine



func GenerateKey() (*Key, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, fmt.Errorf("generate key failed: %v", err)
}

k := Key{privateKey: privateKey}
k.init()
return &k, nil
}



To save the key to a file:



func (k *Key) SavePrivateKey() error {
privateKeyBytes := x509.MarshalPKCS1PrivateKey(k.privateKey)
privateKeyBlock := pem.Block{
Type: "PRIVATE KEY",
Bytes: privateKeyBytes,
}

var writerBuffer bytes.Buffer

err := pem.Encode(&writerBuffer, &privateKeyBlock)
if err != nil {
return fmt.Errorf("encode private key failed: %v", err)
}

fileData := writerBuffer.String()
err = os.WriteFile(Config.PrivateKeyPath, []byte(fileData), 0644)
if err != nil {
return fmt.Errorf("write file failed: %v", err)
}
return nil
}



To load the key from a file:



func LoadPrivateKey() (*Key, error) {
pemFile, err := os.ReadFile(Config.PrivateKeyPath)
if err != nil {
return nil, fmt.Errorf("read file failed: %v", err)
}
pemBlock, _ := pem.Decode(pemFile)
privateKey, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("unmarshal key failed: %v", err)
}

rsaPrivateKey,ok :=privateKey.(*rsa.PrivateKey)
if !ok{
return nil, fmt.Errorf("convert failed: %v", err)
}

k := Key{privateKey: rsaPrivateKey}
k.init()

log.Info("private key loaded %v", Config.PrivateKeyPath)
return &k, nil
}



To get the public key for sending it to other parties:



func (k *Key) GetPublicKeyPem() (string, error) {
publicKeyBytes, err := x509.MarshalPKIXPublicKey(k.privateKey.Public())
if err != nil {
return "", fmt.Errorf("marshal public key failed: %v", err)
}

publicKeyBlock := pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyBytes,
}

var writerBuffer bytes.Buffer

err = pem.Encode(&writerBuffer, &publicKeyBlock)
if err != nil {
return "", fmt.Errorf("encode public key failed: %v", err)
}

return writerBuffer.String(), nil
}



And finally, we can encrypt and decrypt:



func (k *Key) EncryptString(clearText string) (string, error) {
encryptedBytes, err := rsa.EncryptOAEP(
k.hash,
k.random,
&k.privateKey.PublicKey,
[]byte(clearText),
nil,
)

if err != nil {
return "", fmt.Errorf("encrypt failed: %v", err)
}
encryptedBase64 := base64.StdEncoding.EncodeToString(encryptedBytes)
return encryptedBase64, nil
}

func (k *Key) DecryptString(base64Cipher string) (string, error) {
encryptedBytes, err := base64.StdEncoding.DecodeString(base64Cipher)
if err != nil {
return "", fmt.Errorf("base64 decode failed: %v", err)
}

clearTextBytes, err := rsa.DecryptOAEP(
k.hash,
k.random,
k.privateKey,
encryptedBytes,
nil,
)
if err != nil {
return "", fmt.Errorf("decrypt failed: %v", err)
}

clearText := string(clearTextBytes)
return clearText, nil
}




Final Note


This post is part of a series of posts about encryption. The full posts list is below.




No comments:

Post a Comment