Sling Academy
Home/Golang/Understanding and Using the `crypto/rsa` Package for Encryption in Go

Understanding and Using the `crypto/rsa` Package for Encryption in Go

Last updated: November 27, 2024

The crypto/rsa package in Go provides implementations of RSA encryption and decryption, a popular asymmetric cryptographic algorithm. RSA is commonly used for secure data transmission. In this article, we'll explore how to use the `crypto/rsa` package for both encrypting and decrypting data in Go.

What is RSA Encryption?

RSA encryption is an asymmetric cryptography algorithm which uses two keys: a public key for encryption and a private key for decryption. This enables secure data transmission, as only the private key owner can decrypt the data encrypted with the public key.

Setting Up

You can create a new directory to set up your project:

mkdir rsa-encryption
cd rsa-encryption
go mod init rsa-encryption

Generating RSA Keys

In a real setting, you often use of an external tool to generate RSA keys. However, for simplicity, we're going to generate them programmatically using the crypto/rsa and crypto/rand packages in Go. The code below shows how to generate an RSA key pair:

package main

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

func generateRSAKeyPair(bits int) (*rsa.PrivateKey, error) {
    // Generate a new private key
    privateKey, err := rsa.GenerateKey(rand.Reader, bits)
    if err != nil {
        return nil, err
    }
    return privateKey, nil
}

func savePEMKey(fileName string, key *rsa.PrivateKey) error {
    outFile, err := os.Create(fileName)
    if err != nil {
        return err
    }
    defer outFile.Close()

    // Export the private key as PEM
    privateKeyPEM := &pem.Block{
        Type:  "RSA PRIVATE KEY",
        Bytes: x509.MarshalPKCS1PrivateKey(key),
    }
    return pem.Encode(outFile, privateKeyPEM)
}

func main() {
    privateKey, err := generateRSAKeyPair(2048)
    if err != nil {
        fmt.Println(err)
        return
    }
    savePEMKey("private.pem", privateKey)
    fmt.Println("Private key saved to private.pem")
}

This script generates a 2048-bit RSA private key and saves it as a PEM file. Modify these values based on your security requirements.

Encrypting Data

Once your keys are set up, you can encrypt data using the public key. Here is an example in Go:

package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "os"
)

func loadRSAPublicKeyFromFile(fileName string) (*rsa.PublicKey, error) {
    pubKeyBytes, err := ioutil.ReadFile(fileName)
    if err != nil {
        return nil, err
    }

    pubPem, _ := pem.Decode(pubKeyBytes)
    if pubPem == nil {
        return nil, fmt.Errorf("failed to parse PEM block containing the public key")
    }

    pub, err := x509.ParsePKIXPublicKey(pubPem.Bytes)
    if err != nil {
        return nil, err
    }

    switch pub := pub.(type) {
    case *rsa.PublicKey:
        return pub, nil
    default:
        return nil, fmt.Errorf("not an RSA public key")
    }
}

func encryptMessage(publicKey *rsa.PublicKey, message string) ([]byte, error) {
    return rsa.EncryptOAEP(rand.Reader, rand.Reader, publicKey, []byte(message), nil)
}

func main() {
    publicKey, err := loadRSAPublicKeyFromFile("public.pem")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    cipherText, err := encryptMessage(publicKey, "Hello, RSA!")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    fmt.Printf("Cipher Text: %x\n", cipherText)
}

This code snippet loads an RSA public key from a PEM file and uses it to encrypt a message. Note the use of OAEP (Optimal Asymmetric Encryption Padding) for the encryption process.

Decrypting Data

To decrypt the data, the private key is needed, as shown below:

package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "os"
)

func loadRSAPrivateKeyFromFile(fileName string) (*rsa.PrivateKey, error) {
    privKeyBytes, err := ioutil.ReadFile(fileName)
    if err != nil {
        return nil, err
    }

    privPem, _ := pem.Decode(privKeyBytes)
    if privPem == nil {
        return nil, fmt.Errorf("failed to parse PEM block containing the private key")
    }

    return x509.ParsePKCS1PrivateKey(privPem.Bytes)
}

func decryptMessage(privateKey *rsa.PrivateKey, cipherText []byte) (string, error) {
    decryptedBytes, err := rsa.DecryptOAEP(rand.Reader, rand.Reader, privateKey, cipherText, nil)
    if err != nil {
        return "", err
    }
    return string(decryptedBytes), nil
}

func main() {
    privateKey, err := loadRSAPrivateKeyFromFile("private.pem")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    cipherText := ... // encrypted message bytes

    message, err := decryptMessage(privateKey, cipherText)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    fmt.Println("Decrypted Message:", message)
}

The decryption code loads the RSA private key from a PEM file and uses it to decrypt a ciphertext obtained in the previous example.

Conclusion

With the tools provided by the crypto/rsa package, you can seamlessly perform RSA encryption and decryption in Go. This enhances security by allowing you to exchange sensitive information securely across networks. Remember to safeguard your private key as it is critical in maintaining security. Practice integrating these concepts into your applications for secure data handling.

Next Article: How to Implement Secure File Encryption in Go

Previous Article: Creating Self-Signed Certificates in Go

Series: Cryptography and Security in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant