Skip to content

Instantly share code, notes, and snippets.

@cod3smith
Created June 8, 2023 11:34
Show Gist options
  • Save cod3smith/145b780c68ea0f1b24d551b2d1adeed1 to your computer and use it in GitHub Desktop.
Save cod3smith/145b780c68ea0f1b24d551b2d1adeed1 to your computer and use it in GitHub Desktop.

Revisions

  1. cod3smith created this gist Jun 8, 2023.
    238 changes: 238 additions & 0 deletions hybrid.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,238 @@
    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))
    }