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-encryptionGenerating 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.