Skip to content

Instantly share code, notes, and snippets.

@mybigman
Forked from TheAlchemistKE/hybrid.go
Created October 9, 2023 12:00
Show Gist options
  • Save mybigman/12f575f11de87cf575d425243e72724a to your computer and use it in GitHub Desktop.
Save mybigman/12f575f11de87cf575d425243e72724a to your computer and use it in GitHub Desktop.
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
)
func generateRSAKeyPair() (*rsa.PrivateKey, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
return privateKey, nil
}
func savePrivateKeyToPEM(privateKey *rsa.PrivateKey, filename string) error {
keyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
pemBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: keyBytes,
}
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
err = pem.Encode(file, pemBlock)
if err != nil {
return err
}
return nil
}
func loadPrivateKeyFromPEM(filename string) (*rsa.PrivateKey, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
pemData, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
block, _ := pem.Decode(pemData)
if block == nil {
return nil, fmt.Errorf("failed to decode PEM block")
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return privateKey, nil
}
func savePublicKeyToPEM(publicKey *rsa.PublicKey, filename string) error {
keyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return err
}
pemBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: keyBytes,
}
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
err = pem.Encode(file, pemBlock)
if err != nil {
return err
}
return nil
}
func loadPublicKeyFromPEM(filename string) (*rsa.PublicKey, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
pemData, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
block, _ := pem.Decode(pemData)
if block == nil {
return nil, fmt.Errorf("failed to decode PEM block")
}
publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
return publicKey.(*rsa.PublicKey), nil
}
func encryptSymmetricKey(publicKey *rsa.PublicKey, symmetricKey []byte) ([]byte, error) {
encryptedKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, symmetricKey, nil)
if err != nil {
return nil, err
}
return encryptedKey, nil
}
func decryptSymmetricKey(privateKey *rsa.PrivateKey, encryptedKey []byte) ([]byte, error) {
decryptedKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, encryptedKey, nil)
if err != nil {
return nil, err
}
return decryptedKey, nil
}
func encrypt(plaintext []byte, symmetricKey []byte) ([]byte, error) {
block, err := aes.NewCipher(symmetricKey)
if err != nil {
return nil, err
}
paddedPlaintext := padPKCS7(plaintext, block.BlockSize())
ciphertext := make([]byte, len(paddedPlaintext))
mode := cipher.NewCBCEncrypter(block, symmetricKey[:block.BlockSize()])
mode.CryptBlocks(ciphertext, paddedPlaintext)
return ciphertext, nil
}
func decrypt(ciphertext []byte, symmetricKey []byte) ([]byte, error) {
block, err := aes.NewCipher(symmetricKey)
if err != nil {
return nil, err
}
plaintext := make([]byte, len(ciphertext))
mode := cipher.NewCBCDecrypter(block, symmetricKey[:block.BlockSize()])
mode.CryptBlocks(plaintext, ciphertext)
unpaddedPlaintext := unpadPKCS7(plaintext)
return unpaddedPlaintext, nil
}
func padPKCS7(data []byte, blockSize int) []byte {
padding := blockSize - (len(data) % blockSize)
pad := byte(padding)
for i := 0; i < padding; i++ {
data = append(data, pad)
}
return data
}
func unpadPKCS7(data []byte) []byte {
padding := int(data[len(data)-1])
return data[:len(data)-padding]
}
func main() {
// Generate RSA key pair
privateKey, err := generateRSAKeyPair()
if err != nil {
fmt.Println("Key generation error:", err)
return
}
// Save private key to PEM file
err = savePrivateKeyToPEM(privateKey, "private.pem")
if err != nil {
fmt.Println("Private key saving error:", err)
return
}
// Load private key from PEM file
privateKey, err = loadPrivateKeyFromPEM("private.pem")
if err != nil {
fmt.Println("Private key loading error:", err)
return
}
// Get the public key from the private key
publicKey := privateKey.Public().(*rsa.PublicKey)
// Save public key to PEM file
err = savePublicKeyToPEM(publicKey, "public.pem")
if err != nil {
fmt.Println("Public key saving error:", err)
return
}
// Load public key from PEM file
publicKey, err = loadPublicKeyFromPEM("public.pem")
if err != nil {
fmt.Println("Public key loading error:", err)
return
}
// Generate random symmetric key
symmetricKey := make([]byte, 32)
_, err = rand.Read(symmetricKey)
if err != nil {
fmt.Println("Symmetric key generation error:", err)
return
}
// Encrypt the symmetric key using the public key
encryptedKey, err := encryptSymmetricKey(publicKey, symmetricKey)
if err != nil {
fmt.Println("Key encryption error:", err)
return
}
// Encrypt the plaintext using the symmetric key
plaintext := []byte("Hello, world!")
ciphertext, err := encrypt(plaintext, symmetricKey)
if err != nil {
fmt.Println("Encryption error:", err)
return
}
fmt.Println("encrypted key: ", string(ciphertext))
// Decrypt the symmetric key using the private key
decryptedKey, err := decryptSymmetricKey(privateKey, encryptedKey)
if err != nil {
fmt.Println("Key decryption error:", err)
return
}
// Decrypt the ciphertext using the symmetric key
decryptedPlaintext, err := decrypt(ciphertext, decryptedKey)
if err != nil {
fmt.Println("Decryption error:", err)
return
}
fmt.Println("Decrypted plaintext:", string(decryptedPlaintext))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment