Go RSA-OAEP Code Example (Online Runner)

Go RSA-OAEP examples with hash selection, PEM keys, and hybrid file encryption matching the RSA tools.

Online calculator: use the site RSA tool.

Calculation method

Use RSA-OAEP with selectable hash functions for small payloads. For files, use a hybrid scheme: encrypt file bytes with AES-256-CBC and wrap the AES key with RSA-OAEP.

Implementation notes

  • Package: built-in crypto/rsa, crypto/x509, crypto/aes, and crypto/cipher.
  • Implementation: RSA-OAEP supports SHA-1/256/384/512. The file helper emits a JSON package matching the tool format.
  • Notes: RSA-OAEP has a max message size; use it for keys, not large payloads.

Text encryption example

go
package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
)

func generateKeypair(bits int) (string, string, error) {
	priv, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return "", "", err
	}
	privPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
	pubPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PUBLIC KEY", Bytes: x509.MarshalPKCS1PublicKey(&priv.PublicKey)})
	return string(pubPEM), string(privPEM), nil
}

func rsaEncrypt(text string, publicPEM string) (string, error) {
	block, _ := pem.Decode([]byte(publicPEM))
	pub, err := x509.ParsePKCS1PublicKey(block.Bytes)
	if err != nil {
		return "", err
	}
	ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, pub, []byte(text), nil)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(ciphertext), nil
}

func rsaDecrypt(ciphertext string, privatePEM string) (string, error) {
	block, _ := pem.Decode([]byte(privatePEM))
	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return "", err
	}
	data, err := base64.StdEncoding.DecodeString(ciphertext)
	if err != nil {
		return "", err
	}
	plain, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, priv, data, nil)
	if err != nil {
		return "", err
	}
	return string(plain), nil
}

func main() {
	pub, priv, err := generateKeypair(2048)
	if err != nil {
		panic(err)
	}
	ciphertext, err := rsaEncrypt("hello", pub)
	if err != nil {
		panic(err)
	}
	fmt.Println(ciphertext)
	plain, err := rsaDecrypt(ciphertext, priv)
	if err != nil {
		panic(err)
	}
	fmt.Println(plain)
}

File encryption example

go
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"os"
	"path/filepath"
)

type FilePackage struct {
	Version      int    `json:"version"`
	Hash         string `json:"hash"`
	Mode         string `json:"mode"`
	IV           string `json:"iv"`
	EncryptedKey string `json:"encryptedKey"`
	Ciphertext   string `json:"ciphertext"`
	OriginalName string `json:"originalName"`
	MimeType     string `json:"mimeType"`
}

func generateKeypair(bits int) (string, string, error) {
	priv, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return "", "", err
	}
	privPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
	pubPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PUBLIC KEY", Bytes: x509.MarshalPKCS1PublicKey(&priv.PublicKey)})
	return string(pubPEM), string(privPEM), nil
}

func rsaEncryptFile(path string, publicPEM string) (string, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return "", err
	}
	key := make([]byte, 32)
	iv := make([]byte, 16)
	if _, err := rand.Read(key); err != nil {
		return "", err
	}
	if _, err := rand.Read(iv); err != nil {
		return "", err
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	padded := pkcs7Pad(data, block.BlockSize())
	ciphertext := make([]byte, len(padded))
	cipher.NewCBCEncrypter(block, iv).CryptBlocks(ciphertext, padded)

	blockPEM, _ := pem.Decode([]byte(publicPEM))
	pub, err := x509.ParsePKCS1PublicKey(blockPEM.Bytes)
	if err != nil {
		return "", err
	}
	encKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, pub, key, nil)
	if err != nil {
		return "", err
	}

	pkg := FilePackage{
		Version:      1,
		Hash:         "SHA-256",
		Mode:         "AES-256-CBC",
		IV:           hex.EncodeToString(iv),
		EncryptedKey: base64.StdEncoding.EncodeToString(encKey),
		Ciphertext:   base64.StdEncoding.EncodeToString(ciphertext),
		OriginalName: filepath.Base(path),
		MimeType:     "application/octet-stream",
	}
	payload, err := json.MarshalIndent(pkg, "", "  ")
	if err != nil {
		return "", err
	}
	return string(payload), nil
}

func pkcs7Pad(data []byte, blockSize int) []byte {
	padLen := blockSize - (len(data) % blockSize)
	out := make([]byte, len(data)+padLen)
	copy(out, data)
	for i := len(data); i < len(out); i++ {
		out[i] = byte(padLen)
	}
	return out
}

func main() {
	pub, _, err := generateKeypair(2048)
	if err != nil {
		panic(err)
	}
	file, err := os.CreateTemp("", "rsa-file-*.bin")
	if err != nil {
		panic(err)
	}
	defer os.Remove(file.Name())
	file.WriteString("file payload")
	file.Close()

	payload, err := rsaEncryptFile(file.Name(), pub)
	if err != nil {
		panic(err)
	}
	fmt.Println(payload)
}

Complete script (implementation + tests)

go
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"hash"
	"mime"
	"os"
	"path/filepath"
)

type HashAlg string

type CipherEncoding string

const (
	HashSHA1   HashAlg = "SHA-1"
	HashSHA256 HashAlg = "SHA-256"
	HashSHA384 HashAlg = "SHA-384"
	HashSHA512 HashAlg = "SHA-512"
)

const (
	CipherHex    CipherEncoding = "hex"
	CipherBase64 CipherEncoding = "base64"
)

type FilePackage struct {
	Version       int    `json:"version"`
	Hash          string `json:"hash"`
	Mode          string `json:"mode"`
	IV            string `json:"iv"`
	EncryptedKey  string `json:"encryptedKey"`
	Ciphertext    string `json:"ciphertext"`
	OriginalName  string `json:"originalName"`
	MimeType      string `json:"mimeType"`
}

func hashFactory(alg HashAlg) (hash.Hash, error) {
	switch alg {
	case HashSHA1:
		return sha1.New(), nil
	case HashSHA256:
		return sha256.New(), nil
	case HashSHA384:
		return sha512.New384(), nil
	case HashSHA512:
		return sha512.New(), nil
	default:
		return nil, errors.New("unsupported hash")
	}
}

func encodeCiphertext(data []byte, encoding CipherEncoding) string {
	if encoding == CipherHex {
		return hex.EncodeToString(data)
	}
	return base64.StdEncoding.EncodeToString(data)
}

func decodeCiphertext(value string, encoding CipherEncoding) ([]byte, error) {
	if encoding == CipherHex {
		return hex.DecodeString(value)
	}
	return base64.StdEncoding.DecodeString(value)
}

func generateRSAKeypair(bits int) (string, string, error) {
	priv, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return "", "", err
	}
	privBytes := x509.MarshalPKCS1PrivateKey(priv)
	privPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes})

	pubBytes := x509.MarshalPKCS1PublicKey(&priv.PublicKey)
	pubPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PUBLIC KEY", Bytes: pubBytes})

	return string(pubPEM), string(privPEM), nil
}

func parsePublicKey(pemData string) (*rsa.PublicKey, error) {
	block, _ := pem.Decode([]byte(pemData))
	if block == nil {
		return nil, errors.New("invalid public key PEM")
	}
	return x509.ParsePKCS1PublicKey(block.Bytes)
}

func parsePrivateKey(pemData string) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode([]byte(pemData))
	if block == nil {
		return nil, errors.New("invalid private key PEM")
	}
	return x509.ParsePKCS1PrivateKey(block.Bytes)
}

func rsaEncrypt(text, publicPEM string, hashAlg HashAlg, encoding CipherEncoding) (string, error) {
	pub, err := parsePublicKey(publicPEM)
	if err != nil {
		return "", err
	}
	h, err := hashFactory(hashAlg)
	if err != nil {
		return "", err
	}
	ciphertext, err := rsa.EncryptOAEP(h, rand.Reader, pub, []byte(text), nil)
	if err != nil {
		return "", err
	}
	return encodeCiphertext(ciphertext, encoding), nil
}

func rsaDecrypt(ciphertext, privatePEM string, hashAlg HashAlg, encoding CipherEncoding) (string, error) {
	priv, err := parsePrivateKey(privatePEM)
	if err != nil {
		return "", err
	}
	h, err := hashFactory(hashAlg)
	if err != nil {
		return "", err
	}
	cipherBytes, err := decodeCiphertext(ciphertext, encoding)
	if err != nil {
		return "", err
	}
	plain, err := rsa.DecryptOAEP(h, rand.Reader, priv, cipherBytes, nil)
	if err != nil {
		return "", err
	}
	return string(plain), nil
}

func pkcs7Pad(data []byte, blockSize int) []byte {
	padLen := blockSize - (len(data) % blockSize)
	out := make([]byte, len(data)+padLen)
	copy(out, data)
	for i := len(data); i < len(out); i++ {
		out[i] = byte(padLen)
	}
	return out
}

func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
	if len(data) == 0 || len(data)%blockSize != 0 {
		return nil, errors.New("invalid PKCS7 padding")
	}
	padLen := int(data[len(data)-1])
	if padLen == 0 || padLen > blockSize {
		return nil, errors.New("invalid PKCS7 padding")
	}
	for i := 0; i < padLen; i++ {
		if data[len(data)-1-i] != byte(padLen) {
			return nil, errors.New("invalid PKCS7 padding")
		}
	}
	return data[:len(data)-padLen], nil
}

func rsaEncryptFile(path string, publicPEM string, hashAlg HashAlg) (string, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return "", err
	}
	key := make([]byte, 32)
	iv := make([]byte, 16)
	if _, err := rand.Read(key); err != nil {
		return "", err
	}
	if _, err := rand.Read(iv); err != nil {
		return "", err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	ciphertext := make([]byte, len(pkcs7Pad(data, block.BlockSize())))
	cipher.NewCBCEncrypter(block, iv).CryptBlocks(ciphertext, pkcs7Pad(data, block.BlockSize()))

	pub, err := parsePublicKey(publicPEM)
	if err != nil {
		return "", err
	}
	h, err := hashFactory(hashAlg)
	if err != nil {
		return "", err
	}
	encKey, err := rsa.EncryptOAEP(h, rand.Reader, pub, key, nil)
	if err != nil {
		return "", err
	}

	mimeType := mime.TypeByExtension(filepath.Ext(path))
	if mimeType == "" {
		mimeType = "application/octet-stream"
	}
	pkg := FilePackage{
		Version:      1,
		Hash:         string(hashAlg),
		Mode:         "AES-256-CBC",
		IV:           hex.EncodeToString(iv),
		EncryptedKey: base64.StdEncoding.EncodeToString(encKey),
		Ciphertext:   base64.StdEncoding.EncodeToString(ciphertext),
		OriginalName: filepath.Base(path),
		MimeType:     mimeType,
	}
	payload, err := json.MarshalIndent(pkg, "", "  ")
	if err != nil {
		return "", err
	}
	return string(payload), nil
}

func rsaDecryptFile(packageJSON string, privatePEM string, fallbackHash HashAlg) ([]byte, error) {
	var pkg FilePackage
	if err := json.Unmarshal([]byte(packageJSON), &pkg); err != nil {
		return nil, err
	}
	hashAlg := HashAlg(pkg.Hash)
	if hashAlg == "" {
		hashAlg = fallbackHash
	}

	priv, err := parsePrivateKey(privatePEM)
	if err != nil {
		return nil, err
	}
	h, err := hashFactory(hashAlg)
	if err != nil {
		return nil, err
	}
	encKey, err := base64.StdEncoding.DecodeString(pkg.EncryptedKey)
	if err != nil {
		return nil, err
	}
	key, err := rsa.DecryptOAEP(h, rand.Reader, priv, encKey, nil)
	if err != nil {
		return nil, err
	}

	iv, err := hex.DecodeString(pkg.IV)
	if err != nil {
		return nil, err
	}
	ciphertext, err := base64.StdEncoding.DecodeString(pkg.Ciphertext)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	plaintext := make([]byte, len(ciphertext))
	cipher.NewCBCDecrypter(block, iv).CryptBlocks(plaintext, ciphertext)
	return pkcs7Unpad(plaintext, block.BlockSize())
}

func main() {
	pub, priv, err := generateRSAKeypair(2048)
	if err != nil {
		panic(err)
	}

	ciphertext, err := rsaEncrypt("hello", pub, HashSHA256, CipherBase64)
	if err != nil {
		panic(err)
	}
	fmt.Println("cipher=", ciphertext)

	plain, err := rsaDecrypt(ciphertext, priv, HashSHA256, CipherBase64)
	if err != nil {
		panic(err)
	}
	fmt.Println("plain=", plain)

	file, err := os.CreateTemp("", "rsa-file-*.bin")
	if err != nil {
		panic(err)
	}
	defer os.Remove(file.Name())
	file.WriteString("file payload")
	file.Close()

	packageJSON, err := rsaEncryptFile(file.Name(), pub, HashSHA256)
	if err != nil {
		panic(err)
	}
	plaintext, err := rsaDecryptFile(packageJSON, priv, HashSHA256)
	if err != nil {
		panic(err)
	}
	fmt.Println("file=", string(plaintext))
}